mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 10:32:10 +02:00
Compare commits
94 Commits
1.15.2
...
taskbar-ov
Author | SHA1 | Date | |
---|---|---|---|
9238410756 | |||
7210c29cd8 | |||
26d90c0c9b | |||
a03b222a95 | |||
7944a24d3c | |||
cc8459c759 | |||
10074ff92c | |||
173f25bebc | |||
31680fc4ae | |||
e937d43614 | |||
20e29a7975 | |||
ef815dabce | |||
1fb133e6b8 | |||
50b58cd6a6 | |||
01485d7ef9 | |||
b17c6a5ac7 | |||
d2ed2b4a00 | |||
710a7524a1 | |||
2be46464d6 | |||
8d536a6734 | |||
250d502238 | |||
e8de7266d0 | |||
9414f372d7 | |||
b0f9de67cf | |||
9b082e114e | |||
816a5334ac | |||
15a4e10da9 | |||
01b9302b0c | |||
442126a11a | |||
a9c140c0fc | |||
97ad7a3e68 | |||
7d737eefb6 | |||
4ac05b38d3 | |||
651bbbb672 | |||
52da4d8687 | |||
36063ae76a | |||
2fcec2d2cd | |||
762a7fdfb7 | |||
cd7aeaeed2 | |||
6f414d312c | |||
1b5304efb7 | |||
d59375308f | |||
8c9509a906 | |||
fb86d8f3a8 | |||
50e909cb3d | |||
2f54edf7e7 | |||
c251603e1e | |||
4476edb6c3 | |||
28fc67660f | |||
6e8b5a5ce5 | |||
e53681416f | |||
acb5e184e8 | |||
bdbafb3e5c | |||
ac70cf87c6 | |||
93de835ab4 | |||
2ea38b88ce | |||
2c4f2be57d | |||
fa4beea425 | |||
7a976edc82 | |||
bb22c35221 | |||
ff3dc59016 | |||
2e4dd3df3e | |||
b82e5d33f9 | |||
65d56b336b | |||
7836d61173 | |||
898437720b | |||
d9a80d1085 | |||
ab3d8b0ae2 | |||
b865074c32 | |||
2b8ca77c0d | |||
fa8b4e1e7f | |||
a310c5bcc1 | |||
b5dccd6b91 | |||
c2bcb39b38 | |||
4515add0a0 | |||
5b6aaec48c | |||
810e56ca31 | |||
5bcc8ac2e0 | |||
f5bfb35867 | |||
9088b8cd07 | |||
a7d90dc708 | |||
5968b57a01 | |||
94946a9ed6 | |||
561aec5ef0 | |||
e31696d843 | |||
8b33fd2002 | |||
064e43750f | |||
29d2f4f681 | |||
c11b40b94a | |||
e874e1d798 | |||
01244ec632 | |||
671657e2b0 | |||
dff7278e2e | |||
0881328636 |
@@ -7,6 +7,7 @@ namespace TweetDuck.Configuration{
|
|||||||
public const string ArgDataFolder = "-datafolder";
|
public const string ArgDataFolder = "-datafolder";
|
||||||
public const string ArgLogging = "-log";
|
public const string ArgLogging = "-log";
|
||||||
public const string ArgIgnoreGDPR = "-nogdpr";
|
public const string ArgIgnoreGDPR = "-nogdpr";
|
||||||
|
public const string ArgFreeze = "-freeze";
|
||||||
|
|
||||||
// internal args
|
// internal args
|
||||||
public const string ArgRestart = "-restart";
|
public const string ArgRestart = "-restart";
|
||||||
|
@@ -10,21 +10,25 @@ namespace TweetDuck.Configuration{
|
|||||||
sealed class ConfigManager{
|
sealed class ConfigManager{
|
||||||
public UserConfig User { get; }
|
public UserConfig User { get; }
|
||||||
public SystemConfig System { get; }
|
public SystemConfig System { get; }
|
||||||
|
public PluginConfig Plugins { get; }
|
||||||
|
|
||||||
public event EventHandler ProgramRestartRequested;
|
public event EventHandler ProgramRestartRequested;
|
||||||
|
|
||||||
private readonly FileConfigInstance<UserConfig> infoUser;
|
private readonly FileConfigInstance<UserConfig> infoUser;
|
||||||
private readonly FileConfigInstance<SystemConfig> infoSystem;
|
private readonly FileConfigInstance<SystemConfig> infoSystem;
|
||||||
|
private readonly PluginConfigInstance infoPlugins;
|
||||||
|
|
||||||
private readonly IConfigInstance<BaseConfig>[] infoList;
|
private readonly IConfigInstance<BaseConfig>[] infoList;
|
||||||
|
|
||||||
public ConfigManager(){
|
public ConfigManager(){
|
||||||
User = new UserConfig(this);
|
User = new UserConfig(this);
|
||||||
System = new SystemConfig(this);
|
System = new SystemConfig(this);
|
||||||
|
Plugins = new PluginConfig(this);
|
||||||
|
|
||||||
infoList = new IConfigInstance<BaseConfig>[]{
|
infoList = new IConfigInstance<BaseConfig>[]{
|
||||||
infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"),
|
infoUser = new FileConfigInstance<UserConfig>(Program.UserConfigFilePath, User, "program options"),
|
||||||
infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options")
|
infoSystem = new FileConfigInstance<SystemConfig>(Program.SystemConfigFilePath, System, "system options"),
|
||||||
|
infoPlugins = new PluginConfigInstance(Program.PluginConfigFilePath, Plugins)
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO refactor further
|
// TODO refactor further
|
||||||
@@ -51,16 +55,19 @@ namespace TweetDuck.Configuration{
|
|||||||
public void LoadAll(){
|
public void LoadAll(){
|
||||||
infoUser.Load();
|
infoUser.Load();
|
||||||
infoSystem.Load();
|
infoSystem.Load();
|
||||||
|
infoPlugins.Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveAll(){
|
public void SaveAll(){
|
||||||
infoUser.Save();
|
infoUser.Save();
|
||||||
infoSystem.Save();
|
infoSystem.Save();
|
||||||
|
infoPlugins.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReloadAll(){
|
public void ReloadAll(){
|
||||||
infoUser.Reload();
|
infoUser.Reload();
|
||||||
infoSystem.Reload();
|
infoSystem.Reload();
|
||||||
|
infoPlugins.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TriggerProgramRestartRequested(){
|
private void TriggerProgramRestartRequested(){
|
||||||
|
@@ -34,7 +34,7 @@ namespace TweetDuck.Configuration.Instance{
|
|||||||
LoadInternal(attempt > 0);
|
LoadInternal(attempt > 0);
|
||||||
|
|
||||||
if (firstException != null){ // silently log exception that caused a backup restore
|
if (firstException != null){ // silently log exception that caused a backup restore
|
||||||
Program.Reporter.Log(firstException.ToString());
|
Program.Reporter.LogImportant(firstException.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
69
Configuration/Instance/PluginConfigInstance.cs
Normal file
69
Configuration/Instance/PluginConfigInstance.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace TweetDuck.Configuration.Instance{
|
||||||
|
class PluginConfigInstance : IConfigInstance<PluginConfig>{
|
||||||
|
public PluginConfig Instance { get; }
|
||||||
|
|
||||||
|
private readonly string filename;
|
||||||
|
|
||||||
|
public PluginConfigInstance(string filename, PluginConfig instance){
|
||||||
|
this.filename = filename;
|
||||||
|
this.Instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Load(){
|
||||||
|
try{
|
||||||
|
using(StreamReader reader = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
||||||
|
string line = reader.ReadLine();
|
||||||
|
|
||||||
|
if (line == "#Disabled"){
|
||||||
|
HashSet<string> newDisabled = new HashSet<string>();
|
||||||
|
|
||||||
|
while((line = reader.ReadLine()) != null){
|
||||||
|
newDisabled.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance.ReloadSilently(newDisabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(FileNotFoundException){
|
||||||
|
}catch(DirectoryNotFoundException){
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(){
|
||||||
|
try{
|
||||||
|
using(StreamWriter writer = new StreamWriter(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
||||||
|
writer.WriteLine("#Disabled");
|
||||||
|
|
||||||
|
foreach(string identifier in Instance.DisabledPlugins){
|
||||||
|
writer.WriteLine(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reload(){
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset(){
|
||||||
|
try{
|
||||||
|
File.Delete(filename);
|
||||||
|
Instance.ReloadSilently(Instance.ConstructWithDefaults<PluginConfig>().DisabledPlugins);
|
||||||
|
}catch(Exception e){
|
||||||
|
Program.Reporter.HandleException("Plugin Configuration Error", "Could not delete the plugin configuration file.", true, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -124,7 +124,7 @@ namespace TweetDuck.Configuration{
|
|||||||
try{
|
try{
|
||||||
File.Delete(file);
|
File.Delete(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Program.Reporter.Log(e.ToString());
|
Program.Reporter.LogImportant(e.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
Configuration/PluginConfig.cs
Normal file
45
Configuration/PluginConfig.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
|
|
||||||
|
namespace TweetDuck.Configuration{
|
||||||
|
sealed class PluginConfig : ConfigManager.BaseConfig, IPluginConfig{
|
||||||
|
private static readonly string[] DefaultDisabled = {
|
||||||
|
"official/clear-columns",
|
||||||
|
"official/reply-account"
|
||||||
|
};
|
||||||
|
|
||||||
|
// CONFIGURATION
|
||||||
|
|
||||||
|
public IEnumerable<string> DisabledPlugins => disabled;
|
||||||
|
|
||||||
|
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||||
|
|
||||||
|
public void SetEnabled(Plugin plugin, bool enabled){
|
||||||
|
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
||||||
|
PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(Plugin plugin){
|
||||||
|
return !disabled.Contains(plugin.Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReloadSilently(IEnumerable<string> newDisabled){
|
||||||
|
disabled.Clear();
|
||||||
|
disabled.UnionWith(newDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled);
|
||||||
|
|
||||||
|
// END OF CONFIG
|
||||||
|
|
||||||
|
public PluginConfig(ConfigManager configManager) : base(configManager){}
|
||||||
|
|
||||||
|
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){
|
||||||
|
return new PluginConfig(configManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -18,14 +18,15 @@ namespace TweetDuck.Configuration{
|
|||||||
public Size PluginsWindowSize { get; set; } = Size.Empty;
|
public Size PluginsWindowSize { get; set; } = Size.Empty;
|
||||||
|
|
||||||
public bool ExpandLinksOnHover { get; set; } = true;
|
public bool ExpandLinksOnHover { get; set; } = true;
|
||||||
|
public bool FocusDmInput { get; set; } = true;
|
||||||
public bool OpenSearchInFirstColumn { get; set; } = true;
|
public bool OpenSearchInFirstColumn { get; set; } = true;
|
||||||
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
public bool KeepLikeFollowDialogsOpen { get; set; } = true;
|
||||||
public bool BestImageQuality { get; set; } = true;
|
public bool BestImageQuality { get; set; } = true;
|
||||||
public bool EnableAnimatedImages { get; set; } = true;
|
public bool EnableAnimatedImages { get; set; } = true;
|
||||||
|
|
||||||
public bool _enableSmoothScrolling = true;
|
private bool _enableSmoothScrolling = true;
|
||||||
public bool _enableTouchAdjustment = false;
|
private bool _enableTouchAdjustment = false;
|
||||||
public string _customCefArgs = null;
|
private string _customCefArgs = null;
|
||||||
|
|
||||||
public string BrowserPath { get; set; } = null;
|
public string BrowserPath { get; set; } = null;
|
||||||
public bool IgnoreTrackingUrlWarning { get; set; } = false;
|
public bool IgnoreTrackingUrlWarning { get; set; } = false;
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
static class PropertyBridge{
|
static class PropertyBridge{
|
||||||
@@ -10,20 +11,22 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
string Bool(bool value) => value ? "true;" : "false;";
|
string Bool(bool value) => value ? "true;" : "false;";
|
||||||
string Str(string value) => '"'+value+"\";";
|
string Str(string value) => '"'+value+"\";";
|
||||||
|
|
||||||
StringBuilder build = new StringBuilder().Append("(function(x){");
|
UserConfig config = Program.Config.User;
|
||||||
|
StringBuilder build = new StringBuilder(128).Append("(function(x){");
|
||||||
|
|
||||||
build.Append("x.expandLinksOnHover=").Append(Bool(Program.UserConfig.ExpandLinksOnHover));
|
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
|
||||||
|
|
||||||
if (environment == Environment.Browser){
|
if (environment == Environment.Browser){
|
||||||
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
|
build.Append("x.focusDmInput=").Append(Bool(config.FocusDmInput));
|
||||||
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen));
|
build.Append("x.openSearchInFirstColumn=").Append(Bool(config.OpenSearchInFirstColumn));
|
||||||
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
|
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(config.KeepLikeFollowDialogsOpen));
|
||||||
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
|
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));
|
||||||
build.Append("x.translationTarget=").Append(Str(Program.UserConfig.TranslationTarget));
|
build.Append("x.notificationMediaPreviews=").Append(Bool(config.NotificationMediaPreviews));
|
||||||
|
build.Append("x.translationTarget=").Append(Str(config.TranslationTarget));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (environment == Environment.Notification){
|
if (environment == Environment.Notification){
|
||||||
build.Append("x.skipOnLinkClick=").Append(Bool(Program.UserConfig.NotificationSkipOnLinkClick));
|
build.Append("x.skipOnLinkClick=").Append(Bool(config.NotificationSkipOnLinkClick));
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
|
return build.Append("})(window.$TDX=window.$TDX||{})").ToString();
|
||||||
|
@@ -1,13 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Windows.Forms;
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using CefSharp;
|
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Resources;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Bridge{
|
namespace TweetDuck.Core.Bridge{
|
||||||
class TweetDeckBridge{
|
class TweetDeckBridge{
|
||||||
@@ -15,25 +11,10 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
public static string NotificationHeadLayout { get; private set; }
|
public static string NotificationHeadLayout { get; private set; }
|
||||||
public static readonly ContextInfo ContextInfo = new ContextInfo();
|
public static readonly ContextInfo ContextInfo = new ContextInfo();
|
||||||
|
|
||||||
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
|
|
||||||
|
|
||||||
public static void ResetStaticProperties(){
|
public static void ResetStaticProperties(){
|
||||||
FontSize = NotificationHeadLayout = null;
|
FontSize = NotificationHeadLayout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestoreSessionData(IFrame frame){
|
|
||||||
if (SessionData.Count > 0){
|
|
||||||
StringBuilder build = new StringBuilder().Append("window.TD_SESSION={");
|
|
||||||
|
|
||||||
foreach(KeyValuePair<string, string> kvp in SessionData){
|
|
||||||
build.Append(kvp.Key).Append(":'").Append(kvp.Value.Replace("'", "\\'")).Append("',");
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptLoader.ExecuteScript(frame, build.Append("}").ToString(), "gen:session");
|
|
||||||
SessionData.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
private readonly FormNotificationMain notification;
|
private readonly FormNotificationMain notification;
|
||||||
|
|
||||||
@@ -79,12 +60,6 @@ namespace TweetDuck.Core.Bridge{
|
|||||||
public void DisplayTooltip(string text){
|
public void DisplayTooltip(string text){
|
||||||
form.InvokeAsyncSafe(() => form.DisplayTooltip(text));
|
form.InvokeAsyncSafe(() => form.DisplayTooltip(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSessionData(string key, string value){
|
|
||||||
form.InvokeSafe(() => { // do not use InvokeAsyncSafe, return only after invocation
|
|
||||||
SessionData.Add(key, value);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification only
|
// Notification only
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using Microsoft.WindowsAPICodePack.Taskbar;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
@@ -14,13 +15,13 @@ using TweetDuck.Core.Other.Analytics;
|
|||||||
using TweetDuck.Core.Other.Settings.Dialogs;
|
using TweetDuck.Core.Other.Settings.Dialogs;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Enums;
|
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
|
using TweetDuck.Resources;
|
||||||
using TweetDuck.Updates;
|
using TweetDuck.Updates;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
|
||||||
private static UserConfig Config => Program.UserConfig;
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
public bool IsWaiting{
|
public bool IsWaiting{
|
||||||
set{
|
set{
|
||||||
@@ -50,6 +51,7 @@ namespace TweetDuck.Core{
|
|||||||
private readonly FormNotificationTweet notification;
|
private readonly FormNotificationTweet notification;
|
||||||
private readonly ContextMenu contextMenu;
|
private readonly ContextMenu contextMenu;
|
||||||
private readonly UpdateBridge updateBridge;
|
private readonly UpdateBridge updateBridge;
|
||||||
|
private readonly TaskbarIcon taskbarIcon;
|
||||||
|
|
||||||
private bool isLoaded;
|
private bool isLoaded;
|
||||||
private FormWindowState prevState;
|
private FormWindowState prevState;
|
||||||
@@ -63,7 +65,7 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
Text = Program.BrandName;
|
Text = Program.BrandName;
|
||||||
|
|
||||||
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
|
this.plugins = new PluginManager(Program.Config.Plugins, Program.PluginPath);
|
||||||
this.plugins.Reloaded += plugins_Reloaded;
|
this.plugins.Reloaded += plugins_Reloaded;
|
||||||
this.plugins.Executed += plugins_Executed;
|
this.plugins.Executed += plugins_Executed;
|
||||||
this.plugins.Reload();
|
this.plugins.Reload();
|
||||||
@@ -79,13 +81,14 @@ namespace TweetDuck.Core{
|
|||||||
this.updateBridge.UpdateDelayed += updateBridge_UpdateDelayed;
|
this.updateBridge.UpdateDelayed += updateBridge_UpdateDelayed;
|
||||||
this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed;
|
this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed;
|
||||||
|
|
||||||
this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification), updateBridge);
|
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge.Browser(this, notification), updateBridge);
|
||||||
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
|
||||||
|
|
||||||
this.plugins.Register(browser, PluginEnvironment.Browser, this, true);
|
|
||||||
|
|
||||||
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
|
||||||
|
|
||||||
|
this.taskbarIcon = new TaskbarIcon();
|
||||||
|
Shown += (sender, args) => taskbarIcon.UpdateIcon();
|
||||||
|
|
||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
Config.MuteToggled -= Config_MuteToggled;
|
Config.MuteToggled -= Config_MuteToggled;
|
||||||
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
|
||||||
@@ -93,6 +96,7 @@ namespace TweetDuck.Core{
|
|||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
updates.Dispose();
|
updates.Dispose();
|
||||||
contextMenu.Dispose();
|
contextMenu.Dispose();
|
||||||
|
taskbarIcon.Dispose();
|
||||||
|
|
||||||
notificationScreenshotManager?.Dispose();
|
notificationScreenshotManager?.Dispose();
|
||||||
videoPlayer?.Dispose();
|
videoPlayer?.Dispose();
|
||||||
@@ -107,10 +111,6 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
UpdateTray();
|
UpdateTray();
|
||||||
|
|
||||||
if (Config.MuteNotifications){
|
|
||||||
UpdateFormIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.AllowDataCollection){
|
if (Config.AllowDataCollection){
|
||||||
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
|
||||||
}
|
}
|
||||||
@@ -136,10 +136,6 @@ namespace TweetDuck.Core{
|
|||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateFormIcon(){ // TODO fix to show icon in taskbar too
|
|
||||||
Icon = Config.MuteNotifications ? Properties.Resources.icon_muted : Properties.Resources.icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTray(){
|
private void UpdateTray(){
|
||||||
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
|
||||||
}
|
}
|
||||||
@@ -154,6 +150,7 @@ namespace TweetDuck.Core{
|
|||||||
if (!isLoaded)return;
|
if (!isLoaded)return;
|
||||||
|
|
||||||
trayIcon.HasNotifications = false;
|
trayIcon.HasNotifications = false;
|
||||||
|
taskbarIcon.HasNotifications = false;
|
||||||
|
|
||||||
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
|
if (!browser.Enabled){ // when taking a screenshot, the window is unfocused and
|
||||||
browser.Enabled = true; // the browser is disabled; if the user clicks back into
|
browser.Enabled = true; // the browser is disabled; if the user clicks back into
|
||||||
@@ -215,7 +212,6 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
UpdateFormIcon();
|
|
||||||
AnalyticsFile.NotificationMutes.Trigger();
|
AnalyticsFile.NotificationMutes.Trigger();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,7 +364,11 @@ namespace TweetDuck.Core{
|
|||||||
|
|
||||||
public void ReloadToTweetDeck(){
|
public void ReloadToTweetDeck(){
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Resources.ScriptLoader.HotSwap();
|
ScriptLoader.HotSwap();
|
||||||
|
#else
|
||||||
|
if (ModifierKeys.HasFlag(Keys.Shift)){
|
||||||
|
ScriptLoader.ClearCache();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ignoreUpdateCheckError = false;
|
ignoreUpdateCheckError = false;
|
||||||
@@ -492,7 +492,7 @@ namespace TweetDuck.Core{
|
|||||||
FormManager.TryFind<FormSettings>()?.Close();
|
FormManager.TryFind<FormSettings>()?.Close();
|
||||||
|
|
||||||
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins, true)){
|
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins, true)){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){
|
if (!dialog.IsDisposed && dialog.ShowDialog() == DialogResult.OK && !dialog.IsRestarting){ // needs disposal check because the dialog may be closed in constructor
|
||||||
BrowserProcessHandler.UpdatePrefs();
|
BrowserProcessHandler.UpdatePrefs();
|
||||||
FormManager.TryFind<FormPlugins>()?.Close();
|
FormManager.TryFind<FormPlugins>()?.Close();
|
||||||
plugins.Reload(); // also reloads the browser
|
plugins.Reload(); // also reloads the browser
|
||||||
@@ -501,8 +501,12 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
|
public void OnTweetNotification(){ // may be called multiple times, once for each type of notification
|
||||||
if (Config.EnableTrayHighlight && !ContainsFocus){
|
if (!ContainsFocus){
|
||||||
trayIcon.HasNotifications = true;
|
if (Config.EnableTrayHighlight){
|
||||||
|
trayIcon.HasNotifications = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
taskbarIcon.HasNotifications = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
@@ -15,7 +15,9 @@ using TweetDuck.Resources;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
abstract class ContextMenuBase : IContextMenuHandler{
|
abstract class ContextMenuBase : IContextMenuHandler{
|
||||||
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
|
protected static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
|
private static TwitterUtils.ImageQuality ImageQuality => Config.TwitterImageQuality;
|
||||||
|
|
||||||
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
|
||||||
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
|
||||||
@@ -29,8 +31,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509;
|
private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509;
|
||||||
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
|
||||||
|
|
||||||
protected ContextInfo.LinkInfo LastLink { get; private set; }
|
protected ContextInfo.ContextData Context { get; private set; }
|
||||||
protected ContextInfo.ChirpInfo LastChirp { get; private set; }
|
|
||||||
|
|
||||||
private readonly AnalyticsFile.IProvider analytics;
|
private readonly AnalyticsFile.IProvider analytics;
|
||||||
|
|
||||||
@@ -38,19 +39,12 @@ namespace TweetDuck.Core.Handling{
|
|||||||
this.analytics = analytics;
|
this.analytics = analytics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetContextInfo(){
|
|
||||||
LastLink = default(ContextInfo.LinkInfo);
|
|
||||||
LastChirp = default(ContextInfo.ChirpInfo);
|
|
||||||
TweetDeckBridge.ContextInfo.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
|
||||||
ResetContextInfo();
|
Context = TweetDeckBridge.ContextInfo.Reset();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
LastLink = TweetDeckBridge.ContextInfo.Link;
|
Context = TweetDeckBridge.ContextInfo.Create(parameters);
|
||||||
LastChirp = TweetDeckBridge.ContextInfo.Chirp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
|
||||||
@@ -60,15 +54,12 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasTweetImage = LastLink.IsImage;
|
|
||||||
bool hasTweetVideo = LastLink.IsVideo;
|
|
||||||
|
|
||||||
string TextOpen(string name) => "Open "+name+" in browser";
|
string TextOpen(string name) => "Open "+name+" in browser";
|
||||||
string TextCopy(string name) => "Copy "+name+" address";
|
string TextCopy(string name) => "Copy "+name+" address";
|
||||||
string TextSave(string name) => "Save "+name+" as...";
|
string TextSave(string name) => "Save "+name+" as...";
|
||||||
|
|
||||||
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage && !hasTweetVideo){
|
if (Context.Types.HasFlag(ContextInfo.ContextType.Link) && !Context.UnsafeLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
|
||||||
if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){
|
if (TwitterUtils.RegexAccount.IsMatch(Context.UnsafeLinkUrl)){
|
||||||
model.AddItem(MenuOpenLinkUrl, TextOpen("account"));
|
model.AddItem(MenuOpenLinkUrl, TextOpen("account"));
|
||||||
model.AddItem(MenuCopyLinkUrl, TextCopy("account"));
|
model.AddItem(MenuCopyLinkUrl, TextCopy("account"));
|
||||||
model.AddItem(MenuCopyUsername, "Copy account username");
|
model.AddItem(MenuCopyUsername, "Copy account username");
|
||||||
@@ -81,19 +72,19 @@ namespace TweetDuck.Core.Handling{
|
|||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasTweetVideo){
|
if (Context.Types.HasFlag(ContextInfo.ContextType.Video)){
|
||||||
model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
|
model.AddItem(MenuOpenMediaUrl, TextOpen("video"));
|
||||||
model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
|
model.AddItem(MenuCopyMediaUrl, TextCopy("video"));
|
||||||
model.AddItem(MenuSaveMedia, TextSave("video"));
|
model.AddItem(MenuSaveMedia, TextSave("video"));
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
}
|
}
|
||||||
else if (((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage) && parameters.SourceUrl != TweetNotification.AppLogo.Url){
|
else if (Context.Types.HasFlag(ContextInfo.ContextType.Image) && Context.MediaUrl != TweetNotification.AppLogo.Url){
|
||||||
model.AddItem(MenuViewImage, "View image in photo viewer");
|
model.AddItem(MenuViewImage, "View image in photo viewer");
|
||||||
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
|
||||||
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
|
||||||
model.AddItem(MenuSaveMedia, TextSave("image"));
|
model.AddItem(MenuSaveMedia, TextSave("image"));
|
||||||
|
|
||||||
if (LastChirp.Images.Length > 1){
|
if (Context.Chirp.Images.Length > 1){
|
||||||
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
model.AddItem(MenuSaveTweetImages, TextSave("all images"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,64 +97,45 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
switch(commandId){
|
switch(commandId){
|
||||||
case MenuOpenLinkUrl:
|
case MenuOpenLinkUrl:
|
||||||
OpenBrowser(control, LastLink.GetUrl(parameters, true));
|
OpenBrowser(control, Context.LinkUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyLinkUrl:
|
case MenuCopyLinkUrl:
|
||||||
SetClipboardText(control, LastLink.GetUrl(parameters, false));
|
SetClipboardText(control, Context.UnsafeLinkUrl);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyUsername:
|
case MenuCopyUsername: {
|
||||||
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
|
string url = Context.UnsafeLinkUrl;
|
||||||
SetClipboardText(control, match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
|
Match match = TwitterUtils.RegexAccount.Match(url);
|
||||||
|
|
||||||
|
SetClipboardText(control, match.Success ? match.Groups[1].Value : url);
|
||||||
control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger);
|
control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MenuOpenMediaUrl:
|
case MenuOpenMediaUrl:
|
||||||
OpenBrowser(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
OpenBrowser(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuCopyMediaUrl:
|
case MenuCopyMediaUrl:
|
||||||
SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
|
SetClipboardText(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MenuViewImage: {
|
case MenuViewImage: {
|
||||||
void ViewImage(string path){
|
string url = Context.MediaUrl;
|
||||||
string ext = Path.GetExtension(path);
|
|
||||||
|
|
||||||
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
|
|
||||||
WindowsUtils.OpenAssociatedProgram(path);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string url = LastLink.GetMediaSource(parameters);
|
|
||||||
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url) ?? Path.GetRandomFileName());
|
|
||||||
|
|
||||||
control.InvokeAsyncSafe(() => {
|
control.InvokeAsyncSafe(() => {
|
||||||
if (File.Exists(file)){
|
TwitterUtils.ViewImage(url, ImageQuality);
|
||||||
ViewImage(file);
|
analytics.AnalyticsFile.ViewedImages.Trigger();
|
||||||
}
|
|
||||||
else{
|
|
||||||
analytics.AnalyticsFile.ViewedImages.Trigger();
|
|
||||||
|
|
||||||
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
|
|
||||||
ViewImage(file);
|
|
||||||
}, ex => {
|
|
||||||
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MenuSaveMedia: {
|
case MenuSaveMedia: {
|
||||||
bool isVideo = LastLink.IsVideo;
|
bool isVideo = Context.Types.HasFlag(ContextInfo.ContextType.Video);
|
||||||
string url = LastLink.GetMediaSource(parameters);
|
string url = Context.MediaUrl;
|
||||||
string username = LastChirp.Authors.LastOrDefault();
|
string username = Context.Chirp.Authors.LastOrDefault();
|
||||||
|
|
||||||
control.InvokeAsyncSafe(() => {
|
control.InvokeAsyncSafe(() => {
|
||||||
if (isVideo){
|
if (isVideo){
|
||||||
@@ -180,8 +152,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MenuSaveTweetImages: {
|
case MenuSaveTweetImages: {
|
||||||
string[] urls = LastChirp.Images;
|
string[] urls = Context.Chirp.Images;
|
||||||
string username = LastChirp.Authors.LastOrDefault();
|
string username = Context.Chirp.Authors.LastOrDefault();
|
||||||
|
|
||||||
control.InvokeAsyncSafe(() => {
|
control.InvokeAsyncSafe(() => {
|
||||||
TwitterUtils.DownloadImages(urls, username, ImageQuality);
|
TwitterUtils.DownloadImages(urls, username, ImageQuality);
|
||||||
@@ -212,7 +184,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
|
||||||
ResetContextInfo();
|
Context = TweetDeckBridge.ContextInfo.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
@@ -56,12 +57,12 @@ namespace TweetDuck.Core.Handling{
|
|||||||
InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column");
|
InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && !isSelecting && !isEditing){
|
if (Context.Types.HasFlag(ContextInfo.ContextType.Chirp) && !isSelecting && !isEditing){
|
||||||
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
|
||||||
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
|
||||||
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(LastChirp.QuoteUrl)){
|
if (!string.IsNullOrEmpty(Context.Chirp.QuoteUrl)){
|
||||||
model.AddSeparator();
|
model.AddSeparator();
|
||||||
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||||
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||||
@@ -77,7 +78,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
|
|
||||||
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser);
|
||||||
globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
|
globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications);
|
||||||
globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications);
|
globalMenu.SetChecked(MenuMute, Config.MuteNotifications);
|
||||||
globalMenu.AddSeparator();
|
globalMenu.AddSeparator();
|
||||||
|
|
||||||
globalMenu.AddItem(MenuSettings, TitleSettings);
|
globalMenu.AddItem(MenuSettings, TitleSettings);
|
||||||
@@ -119,11 +120,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenTweetUrl:
|
case MenuOpenTweetUrl:
|
||||||
OpenBrowser(form, LastChirp.TweetUrl);
|
OpenBrowser(form, Context.Chirp.TweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyTweetUrl:
|
case MenuCopyTweetUrl:
|
||||||
SetClipboardText(form, LastChirp.TweetUrl);
|
SetClipboardText(form, Context.Chirp.TweetUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuScreenshotTweet:
|
case MenuScreenshotTweet:
|
||||||
@@ -131,11 +132,11 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuOpenQuotedTweetUrl:
|
case MenuOpenQuotedTweetUrl:
|
||||||
OpenBrowser(form, LastChirp.QuoteUrl);
|
OpenBrowser(form, Context.Chirp.QuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuCopyQuotedTweetUrl:
|
case MenuCopyQuotedTweetUrl:
|
||||||
SetClipboardText(form, LastChirp.QuoteUrl);
|
SetClipboardText(form, Context.Chirp.QuoteUrl);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MenuWriteApplyROT13:
|
case MenuWriteApplyROT13:
|
||||||
@@ -163,7 +164,7 @@ namespace TweetDuck.Core.Handling{
|
|||||||
menu.MenuItems.Add(TitleAboutProgram, (sender, args) => form.OpenAbout());
|
menu.MenuItems.Add(TitleAboutProgram, (sender, args) => form.OpenAbout());
|
||||||
|
|
||||||
menu.Popup += (sender, args) => {
|
menu.Popup += (sender, args) => {
|
||||||
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
|
menu.MenuItems[1].Checked = Config.MuteNotifications;
|
||||||
form.AnalyticsFile.BrowserContextMenus.Trigger();
|
form.AnalyticsFile.BrowserContextMenus.Trigger();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,8 +172,8 @@ namespace TweetDuck.Core.Handling{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void ToggleMuteNotifications(){
|
private static void ToggleMuteNotifications(){
|
||||||
Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
|
Config.MuteNotifications = !Config.MuteNotifications;
|
||||||
Program.UserConfig.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling.General{
|
namespace TweetDuck.Core.Handling.General{
|
||||||
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
sealed class BrowserProcessHandler : IBrowserProcessHandler{
|
||||||
@@ -9,10 +10,12 @@ namespace TweetDuck.Core.Handling.General{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdatePrefsInternal(){
|
private static void UpdatePrefsInternal(){
|
||||||
|
UserConfig config = Program.Config.User;
|
||||||
|
|
||||||
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
|
||||||
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
|
ctx.SetPreference("browser.enable_spellchecking", config.EnableSpellCheck, out string _);
|
||||||
ctx.SetPreference("spellcheck.dictionary", Program.UserConfig.SpellCheckLanguage, out string _);
|
ctx.SetPreference("spellcheck.dictionary", config.SpellCheckLanguage, out string _);
|
||||||
ctx.SetPreference("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _);
|
ctx.SetPreference("settings.a11y.animation_policy", config.EnableAnimatedImages ? "allowed" : "none", out string _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
Core/Handling/KeyboardHandlerBase.cs
Normal file
47
Core/Handling/KeyboardHandlerBase.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using System.Windows.Forms;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Core.Controls;
|
||||||
|
using TweetDuck.Core.Other;
|
||||||
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
class KeyboardHandlerBase : IKeyboardHandler{
|
||||||
|
protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||||
|
if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){
|
||||||
|
if (BrowserUtils.HasDevTools){
|
||||||
|
browser.ShowDevTools();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
browserControl.AsControl().InvokeSafe(() => {
|
||||||
|
string extraMessage;
|
||||||
|
|
||||||
|
if (Program.IsPortable){
|
||||||
|
extraMessage = "Please download the portable installer, select the folder with your current installation of TweetDuck Portable, and tick 'Install dev tools' during the installation process.";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
extraMessage = "Please download the installer, and tick 'Install dev tools' during the installation process. The installer will automatically find and update your current installation of TweetDuck.";
|
||||||
|
}
|
||||||
|
|
||||||
|
FormMessage.Information("Dev Tools", "You do not have dev tools installed. "+extraMessage, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
||||||
|
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
||||||
|
return HandleRawKey(browserControl, browser, (Keys)windowsKeyCode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,19 +2,19 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
sealed class KeyboardHandlerBrowser : IKeyboardHandler{
|
sealed class KeyboardHandlerBrowser : KeyboardHandlerBase{
|
||||||
private readonly FormBrowser form;
|
private readonly FormBrowser form;
|
||||||
|
|
||||||
public KeyboardHandlerBrowser(FormBrowser form){
|
public KeyboardHandlerBrowser(FormBrowser form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||||
return type == KeyType.RawKeyDown && form.ProcessBrowserKey((Keys)windowsKeyCode);
|
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
return form.ProcessBrowserKey(key);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling {
|
namespace TweetDuck.Core.Handling {
|
||||||
sealed class KeyboardHandlerNotification : IKeyboardHandler{
|
sealed class KeyboardHandlerNotification : KeyboardHandlerBase{
|
||||||
private readonly FormNotificationBase notification;
|
private readonly FormNotificationBase notification;
|
||||||
|
|
||||||
public KeyboardHandlerNotification(FormNotificationBase notification){
|
public KeyboardHandlerNotification(FormNotificationBase notification){
|
||||||
@@ -15,31 +15,30 @@ namespace TweetDuck.Core.Handling {
|
|||||||
notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
|
notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){
|
protected override bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
|
||||||
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
|
if (base.HandleRawKey(browserControl, browser, key, modifiers)){
|
||||||
switch((Keys)windowsKeyCode){
|
return true;
|
||||||
case Keys.Enter:
|
|
||||||
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
|
||||||
TriggerKeyboardShortcutAnalytics();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Keys.Escape:
|
|
||||||
notification.InvokeAsyncSafe(notification.HideNotification);
|
|
||||||
TriggerKeyboardShortcutAnalytics();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case Keys.Space:
|
|
||||||
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
|
||||||
TriggerKeyboardShortcutAnalytics();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
switch(key){
|
||||||
}
|
case Keys.Enter:
|
||||||
|
notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
|
return true;
|
||||||
|
|
||||||
bool IKeyboardHandler.OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey){
|
case Keys.Escape:
|
||||||
return false;
|
notification.InvokeAsyncSafe(notification.HideNotification);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Keys.Space:
|
||||||
|
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
|
||||||
|
TriggerKeyboardShortcutAnalytics();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,8 @@
|
|||||||
using System.Collections.Specialized;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.Handler;
|
using CefSharp.Handler;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
@@ -6,6 +10,36 @@ using TweetDuck.Core.Utils;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
class RequestHandlerBase : DefaultRequestHandler{
|
class RequestHandlerBase : DefaultRequestHandler{
|
||||||
|
private static readonly Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$", RegexOptions.Compiled);
|
||||||
|
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
|
||||||
|
|
||||||
|
public static void LoadResourceRewriteRules(string rules){
|
||||||
|
if (string.IsNullOrEmpty(rules)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TweetDeckHashes.Clear();
|
||||||
|
|
||||||
|
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
|
||||||
|
string[] split = rule.Split('=');
|
||||||
|
|
||||||
|
if (split.Length == 2){
|
||||||
|
string key = split[0];
|
||||||
|
string hash = split[1];
|
||||||
|
|
||||||
|
if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
|
||||||
|
TweetDeckHashes.Add(key, hash);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new ArgumentException("Invalid hash characters: "+rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new ArgumentException("A rule must have exactly one '=' character: "+rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly bool autoReload;
|
private readonly bool autoReload;
|
||||||
|
|
||||||
public RequestHandlerBase(bool autoReload){
|
public RequestHandlerBase(bool autoReload){
|
||||||
@@ -26,6 +60,26 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
|
if ((request.ResourceType == ResourceType.Script || request.ResourceType == ResourceType.Stylesheet) && TweetDeckHashes.Count > 0){
|
||||||
|
string url = request.Url;
|
||||||
|
Match match = TweetDeckResourceUrl.Match(url);
|
||||||
|
|
||||||
|
if (match.Success && TweetDeckHashes.TryGetValue($"{match.Groups[1]}.{match.Groups[3]}", out string hash)){
|
||||||
|
if (match.Groups[2].Value == hash){
|
||||||
|
Program.Reporter.LogVerbose("[RequestHandlerBase] Accepting " + url);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Program.Reporter.LogVerbose("[RequestHandlerBase] Replacing " + url + " hash with " + hash);
|
||||||
|
request.Url = TweetDeckResourceUrl.Replace(url, $"/dist/$1.{hash}.$3");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
public override void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){
|
||||||
if (autoReload){
|
if (autoReload){
|
||||||
browser.Reload();
|
browser.Reload();
|
||||||
|
@@ -1,26 +1,35 @@
|
|||||||
// Uncomment to force TweetDeck to load a predefined version of the vendor/bundle scripts
|
using System.Collections.Specialized;
|
||||||
// #define FREEZE_TWEETDECK_SCRIPTS
|
|
||||||
|
|
||||||
using System.Collections.Specialized;
|
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using TweetDuck.Core.Handling.Filters;
|
using TweetDuck.Core.Handling.Filters;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
#if FREEZE_TWEETDECK_SCRIPTS
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Handling{
|
namespace TweetDuck.Core.Handling{
|
||||||
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
sealed class RequestHandlerBrowser : RequestHandlerBase{
|
||||||
|
private const string UrlVendorResource = "/dist/vendor";
|
||||||
|
private const string UrlLoadingSpinner = "/backgrounds/spinner_blue";
|
||||||
|
|
||||||
public string BlockNextUserNavUrl { get; set; }
|
public string BlockNextUserNavUrl { get; set; }
|
||||||
|
|
||||||
public RequestHandlerBrowser() : base(true){}
|
public RequestHandlerBrowser() : base(true){}
|
||||||
|
|
||||||
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
|
||||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
|
if (request.ResourceType == ResourceType.MainFrame){
|
||||||
callback.Dispose();
|
if (request.Url.EndsWith("//twitter.com/")){
|
||||||
return CefReturnValue.Cancel;
|
request.Url = TwitterUtils.TweetDeckURL; // redirect plain twitter.com requests, fixes bugs with login 2FA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (request.ResourceType == ResourceType.Script){
|
||||||
|
string url = request.Url;
|
||||||
|
|
||||||
|
if (url.Contains("analytics.")){
|
||||||
|
callback.Dispose();
|
||||||
|
return CefReturnValue.Cancel;
|
||||||
|
}
|
||||||
|
else if (url.Contains(UrlVendorResource)){
|
||||||
|
NameValueCollection headers = request.Headers;
|
||||||
|
headers["Accept-Encoding"] = "identity";
|
||||||
|
request.Headers = headers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
@@ -36,58 +45,18 @@ namespace TweetDuck.Core.Handling{
|
|||||||
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FREEZE_TWEETDECK_SCRIPTS
|
|
||||||
private static readonly Regex TweetDeckScriptUrl = new Regex(@"/dist/(.*?)\.(.*?)\.js$", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(2){
|
|
||||||
{ "vendor", "942c0a20e8" },
|
|
||||||
{ "bundle", "1bd75b5854" }
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
if (request.ResourceType == ResourceType.Image && request.Url.Contains("/backgrounds/spinner_blue")){
|
if (request.ResourceType == ResourceType.Image && request.Url.Contains(UrlLoadingSpinner)){
|
||||||
request.Url = TwitterUtils.LoadingSpinner.Url;
|
request.Url = TwitterUtils.LoadingSpinner.Url;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#if FREEZE_TWEETDECK_SCRIPTS
|
|
||||||
else if (request.ResourceType == ResourceType.Script){
|
|
||||||
Match match = TweetDeckScriptUrl.Match(request.Url);
|
|
||||||
|
|
||||||
if (match.Success && TweetDeckHashes.TryGetValue(match.Groups[1].Value, out string hash)){
|
|
||||||
if (match.Groups[2].Value == hash){
|
|
||||||
System.Diagnostics.Debug.WriteLine($"accepting {request.Url}");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"rewriting {request.Url} to {hash}");
|
|
||||||
request.Url = TweetDeckScriptUrl.Replace(request.Url, "/dist/$1."+hash+".js");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
return base.OnResourceResponse(browserControl, browser, frame, request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
public override IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
|
||||||
if (request.ResourceType == ResourceType.Script && request.Url.Contains("/dist/vendor")){
|
if (request.ResourceType == ResourceType.Script && request.Url.Contains(UrlVendorResource) && int.TryParse(response.ResponseHeaders["Content-Length"], out int totalBytes)){
|
||||||
NameValueCollection headers = response.ResponseHeaders;
|
return new ResponseFilterVendor(totalBytes);
|
||||||
|
|
||||||
if (int.TryParse(headers["x-ton-expected-size"], out int totalBytes)){
|
|
||||||
return new ResponseFilterVendor(totalBytes);
|
|
||||||
}
|
|
||||||
#if DEBUG
|
|
||||||
else{
|
|
||||||
System.Diagnostics.Debug.WriteLine($"Missing uncompressed size header in {request.Url}");
|
|
||||||
|
|
||||||
foreach(string key in headers){
|
|
||||||
System.Diagnostics.Debug.WriteLine($" {key}: {headers[key]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
System.Diagnostics.Debugger.Break();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
return base.GetResourceResponseFilter(browserControl, browser, frame, request, response);
|
||||||
|
43
Core/Handling/ResourceHandlerFactory.cs
Normal file
43
Core/Handling/ResourceHandlerFactory.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using CefSharp;
|
||||||
|
using TweetDuck.Data;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Handling{
|
||||||
|
sealed class ResourceHandlerFactory : IResourceHandlerFactory{
|
||||||
|
public bool HasHandlers => !handlers.IsEmpty;
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, IResourceHandler> handlers = new ConcurrentDictionary<string, IResourceHandler>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request){
|
||||||
|
try{
|
||||||
|
return handlers.TryGetValue(request.Url, out IResourceHandler handler) ? handler : null;
|
||||||
|
}finally{
|
||||||
|
request.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// registration
|
||||||
|
|
||||||
|
public bool RegisterHandler(string url, IResourceHandler handler){
|
||||||
|
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
|
||||||
|
handlers.AddOrUpdate(uri.AbsoluteUri, handler, (key, prev) => handler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RegisterHandler(ResourceLink link){
|
||||||
|
return RegisterHandler(link.Url, link.Handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UnregisterHandler(string url){
|
||||||
|
return handlers.TryRemove(url, out IResourceHandler _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UnregisterHandler(ResourceLink link){
|
||||||
|
return UnregisterHandler(link.Url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -28,7 +28,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void RefreshTimer(){
|
public static void RefreshTimer(){
|
||||||
bool shouldRun = Program.SystemConfig.ClearCacheAutomatically && !ClearOnExit;
|
bool shouldRun = Program.Config.System.ClearCacheAutomatically && !ClearOnExit;
|
||||||
|
|
||||||
if (!shouldRun && AutoClearTimer != null){
|
if (!shouldRun && AutoClearTimer != null){
|
||||||
AutoClearTimer.Dispose();
|
AutoClearTimer.Dispose();
|
||||||
@@ -38,7 +38,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
AutoClearTimer = new Timer(state => {
|
AutoClearTimer = new Timer(state => {
|
||||||
if (AutoClearTimer != null){
|
if (AutoClearTimer != null){
|
||||||
try{
|
try{
|
||||||
if (CalculateCacheSize() >= Program.SystemConfig.ClearCacheThreshold*1024L*1024L){
|
if (CalculateCacheSize() >= Program.Config.System.ClearCacheThreshold*1024L*1024L){
|
||||||
SetClearOnExit();
|
SetClearOnExit();
|
||||||
}
|
}
|
||||||
}catch(Exception){
|
}catch(Exception){
|
||||||
@@ -54,6 +54,14 @@ namespace TweetDuck.Core.Management{
|
|||||||
RefreshTimer();
|
RefreshTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void TryClearNow(){
|
||||||
|
try{
|
||||||
|
Directory.Delete(CacheFolder, true);
|
||||||
|
}catch{
|
||||||
|
// welp, too bad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void Exit(){
|
public static void Exit(){
|
||||||
if (AutoClearTimer != null){
|
if (AutoClearTimer != null){
|
||||||
AutoClearTimer.Dispose();
|
AutoClearTimer.Dispose();
|
||||||
@@ -61,11 +69,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ClearOnExit){
|
if (ClearOnExit){
|
||||||
try{
|
TryClearNow();
|
||||||
Directory.Delete(CacheFolder, true);
|
|
||||||
}catch{
|
|
||||||
// welp, too bad
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,49 +1,54 @@
|
|||||||
using CefSharp;
|
using System;
|
||||||
|
using CefSharp;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Management{
|
namespace TweetDuck.Core.Management{
|
||||||
sealed class ContextInfo{
|
sealed class ContextInfo{
|
||||||
public LinkInfo Link { get; private set; }
|
private LinkInfo link;
|
||||||
public ChirpInfo Chirp { get; private set; }
|
private ChirpInfo? chirp;
|
||||||
|
|
||||||
public ContextInfo(){
|
public ContextInfo(){
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLink(string type, string url){
|
public void SetLink(string type, string url){
|
||||||
Link = string.IsNullOrEmpty(url) ? new LinkInfo() : new LinkInfo(type, url);
|
link = string.IsNullOrEmpty(url) ? null : new LinkInfo(type, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
|
||||||
Chirp = new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
chirp = string.IsNullOrEmpty(tweetUrl) ? (ChirpInfo?)null : new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset(){
|
public ContextData Reset(){
|
||||||
Link = new LinkInfo();
|
link = null;
|
||||||
Chirp = new ChirpInfo();
|
chirp = null;
|
||||||
|
return ContextData.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextData Create(IContextMenuParams parameters){
|
||||||
|
ContextData.Builder builder = new ContextData.Builder();
|
||||||
|
builder.AddContext(parameters);
|
||||||
|
|
||||||
|
if (link != null){
|
||||||
|
builder.AddOverride(link.Type, link.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chirp.HasValue){
|
||||||
|
builder.AddChirp(chirp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data structures
|
// Data structures
|
||||||
|
|
||||||
public struct LinkInfo{
|
private sealed class LinkInfo{
|
||||||
public bool IsLink => type == "link";
|
public string Type { get; }
|
||||||
public bool IsImage => type == "image";
|
public string Url { get; }
|
||||||
public bool IsVideo => type == "video";
|
|
||||||
|
|
||||||
public string GetUrl(IContextMenuParams parameters, bool safe){
|
|
||||||
return IsLink ? url : (safe ? parameters.LinkUrl : parameters.UnfilteredLinkUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetMediaSource(IContextMenuParams parameters){
|
|
||||||
return IsImage || IsVideo ? url : parameters.SourceUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly string type;
|
|
||||||
private readonly string url;
|
|
||||||
|
|
||||||
public LinkInfo(string type, string url){
|
public LinkInfo(string type, string url){
|
||||||
this.type = type;
|
this.Type = type;
|
||||||
this.url = url;
|
this.Url = url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,5 +69,93 @@ namespace TweetDuck.Core.Management{
|
|||||||
this.chirpImages = chirpImages;
|
this.chirpImages = chirpImages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constructed context
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ContextType{
|
||||||
|
Unknown = 0,
|
||||||
|
Link = 0b0001,
|
||||||
|
Image = 0b0010,
|
||||||
|
Video = 0b0100,
|
||||||
|
Chirp = 0b1000
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ContextData{
|
||||||
|
public static readonly ContextData Empty = new Builder().Build();
|
||||||
|
|
||||||
|
public ContextType Types { get; }
|
||||||
|
|
||||||
|
public string LinkUrl { get; }
|
||||||
|
public string UnsafeLinkUrl { get; }
|
||||||
|
public string MediaUrl { get; }
|
||||||
|
|
||||||
|
public ChirpInfo Chirp { get; }
|
||||||
|
|
||||||
|
public ContextData(ContextType types, string linkUrl, string unsafeLinkUrl, string mediaUrl, ChirpInfo chirp){
|
||||||
|
Types = types;
|
||||||
|
LinkUrl = linkUrl;
|
||||||
|
UnsafeLinkUrl = unsafeLinkUrl;
|
||||||
|
MediaUrl = mediaUrl;
|
||||||
|
Chirp = chirp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Builder{
|
||||||
|
private ContextType types = ContextType.Unknown;
|
||||||
|
|
||||||
|
private string linkUrl = string.Empty;
|
||||||
|
private string unsafeLinkUrl = string.Empty;
|
||||||
|
private string mediaUrl = string.Empty;
|
||||||
|
|
||||||
|
private ChirpInfo chirp = default(ChirpInfo);
|
||||||
|
|
||||||
|
public void AddContext(IContextMenuParams parameters){
|
||||||
|
ContextMenuType flags = parameters.TypeFlags;
|
||||||
|
|
||||||
|
if (flags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){
|
||||||
|
types |= ContextType.Image;
|
||||||
|
types &= ~ContextType.Video;
|
||||||
|
mediaUrl = parameters.SourceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.HasFlag(ContextMenuType.Link)){
|
||||||
|
types |= ContextType.Link;
|
||||||
|
linkUrl = parameters.LinkUrl;
|
||||||
|
unsafeLinkUrl = parameters.UnfilteredLinkUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddOverride(string type, string url){
|
||||||
|
switch(type){
|
||||||
|
case "link":
|
||||||
|
types |= ContextType.Link;
|
||||||
|
linkUrl = url;
|
||||||
|
unsafeLinkUrl = url;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "image":
|
||||||
|
types |= ContextType.Image;
|
||||||
|
types &= ~(ContextType.Video | ContextType.Link);
|
||||||
|
mediaUrl = url;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "video":
|
||||||
|
types |= ContextType.Video;
|
||||||
|
types &= ~(ContextType.Image | ContextType.Link);
|
||||||
|
mediaUrl = url;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddChirp(ChirpInfo chirp){
|
||||||
|
this.types |= ContextType.Chirp;
|
||||||
|
this.chirp = chirp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContextData Build(){
|
||||||
|
return new ContextData(types, linkUrl, unsafeLinkUrl, mediaUrl, chirp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
@@ -9,6 +10,8 @@ using TweetLib.Communication;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Management{
|
namespace TweetDuck.Core.Management{
|
||||||
sealed class VideoPlayer : IDisposable{
|
sealed class VideoPlayer : IDisposable{
|
||||||
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
public bool Running => currentInstance != null && currentInstance.Running;
|
public bool Running => currentInstance != null && currentInstance.Running;
|
||||||
|
|
||||||
public event EventHandler ProcessExited;
|
public event EventHandler ProcessExited;
|
||||||
@@ -37,7 +40,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
|
|
||||||
if ((process = Process.Start(new ProcessStartInfo{
|
if ((process = Process.Start(new ProcessStartInfo{
|
||||||
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
|
||||||
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
|
Arguments = $"{owner.Handle} {(int)Math.Floor(100F*owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
})) != null){
|
})) != null){
|
||||||
@@ -68,9 +71,9 @@ namespace TweetDuck.Core.Management{
|
|||||||
owner.InvokeSafe(() => {
|
owner.InvokeSafe(() => {
|
||||||
switch(e.Key){
|
switch(e.Key){
|
||||||
case "vol":
|
case "vol":
|
||||||
if (int.TryParse(e.Data, out int volume) && volume != Program.UserConfig.VideoPlayerVolume){
|
if (int.TryParse(e.Data, out int volume) && volume != Config.VideoPlayerVolume){
|
||||||
Program.UserConfig.VideoPlayerVolume = volume;
|
Config.VideoPlayerVolume = volume;
|
||||||
Program.UserConfig.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -132,7 +135,7 @@ namespace TweetDuck.Core.Management{
|
|||||||
|
|
||||||
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
|
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
|
||||||
if (!string.IsNullOrEmpty(e.Data)){
|
if (!string.IsNullOrEmpty(e.Data)){
|
||||||
Program.Reporter.Log("[VideoPlayer] "+e.Data);
|
Program.Reporter.LogVerbose("[VideoPlayer] "+e.Data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,11 +8,11 @@ using TweetDuck.Resources;
|
|||||||
namespace TweetDuck.Core.Notification.Example{
|
namespace TweetDuck.Core.Notification.Example{
|
||||||
sealed class FormNotificationExample : FormNotificationMain{
|
sealed class FormNotificationExample : FormNotificationMain{
|
||||||
public override bool RequiresResize => true;
|
public override bool RequiresResize => true;
|
||||||
protected override bool CanDragWindow => Program.UserConfig.NotificationPosition == TweetNotification.Position.Custom;
|
protected override bool CanDragWindow => Config.NotificationPosition == TweetNotification.Position.Custom;
|
||||||
|
|
||||||
protected override FormBorderStyle NotificationBorderStyle{
|
protected override FormBorderStyle NotificationBorderStyle{
|
||||||
get{
|
get{
|
||||||
if (Program.UserConfig.NotificationSize == TweetNotification.Size.Custom){
|
if (Config.NotificationSize == TweetNotification.Size.Custom){
|
||||||
switch(base.NotificationBorderStyle){
|
switch(base.NotificationBorderStyle){
|
||||||
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable;
|
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable;
|
||||||
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow;
|
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow;
|
||||||
@@ -23,6 +23,8 @@ namespace TweetDuck.Core.Notification.Example{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override string BodyClasses => base.BodyClasses+" td-example";
|
||||||
|
|
||||||
public event EventHandler Ready;
|
public event EventHandler Ready;
|
||||||
|
|
||||||
private readonly TweetNotification exampleNotification;
|
private readonly TweetNotification exampleNotification;
|
||||||
@@ -36,7 +38,7 @@ namespace TweetDuck.Core.Notification.Example{
|
|||||||
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
exampleNotification = TweetNotification.Example(exampleTweetHTML, 176);
|
exampleNotification = new TweetNotification(string.Empty, string.Empty, "Home", exampleTweetHTML, 176, string.Empty, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
|
@@ -10,7 +10,9 @@ using TweetDuck.Core.Other.Analytics;
|
|||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
|
abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
|
||||||
|
protected static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
protected static int FontSizeLevel{
|
protected static int FontSizeLevel{
|
||||||
get{
|
get{
|
||||||
switch(TweetDeckBridge.FontSize){
|
switch(TweetDeckBridge.FontSize){
|
||||||
@@ -25,19 +27,18 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
protected virtual Point PrimaryLocation{
|
protected virtual Point PrimaryLocation{
|
||||||
get{
|
get{
|
||||||
UserConfig config = Program.UserConfig;
|
|
||||||
Screen screen;
|
Screen screen;
|
||||||
|
|
||||||
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
|
if (Config.NotificationDisplay > 0 && Config.NotificationDisplay <= Screen.AllScreens.Length){
|
||||||
screen = Screen.AllScreens[config.NotificationDisplay-1];
|
screen = Screen.AllScreens[Config.NotificationDisplay-1];
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
screen = Screen.FromControl(owner);
|
screen = Screen.FromControl(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
int edgeDist = config.NotificationEdgeDistance;
|
int edgeDist = Config.NotificationEdgeDistance;
|
||||||
|
|
||||||
switch(config.NotificationPosition){
|
switch(Config.NotificationPosition){
|
||||||
case TweetNotification.Position.TopLeft:
|
case TweetNotification.Position.TopLeft:
|
||||||
return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist);
|
return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist);
|
||||||
|
|
||||||
@@ -51,12 +52,12 @@ namespace TweetDuck.Core.Notification{
|
|||||||
return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
||||||
|
|
||||||
case TweetNotification.Position.Custom:
|
case TweetNotification.Position.Custom:
|
||||||
if (!config.IsCustomNotificationPositionSet){
|
if (!Config.IsCustomNotificationPositionSet){
|
||||||
config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
Config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
||||||
config.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.CustomNotificationPosition;
|
return Config.CustomNotificationPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Location;
|
return Location;
|
||||||
@@ -93,7 +94,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
protected override bool ShowWithoutActivation => true;
|
protected override bool ShowWithoutActivation => true;
|
||||||
|
|
||||||
protected float DpiScale { get; }
|
protected float DpiScale { get; }
|
||||||
protected double SizeScale => DpiScale*Program.UserConfig.ZoomLevel/100.0;
|
protected double SizeScale => DpiScale*Config.ZoomLevel/100.0;
|
||||||
|
|
||||||
protected readonly FormBrowser owner;
|
protected readonly FormBrowser owner;
|
||||||
protected readonly ChromiumWebBrowser browser;
|
protected readonly ChromiumWebBrowser browser;
|
||||||
@@ -120,18 +121,21 @@ namespace TweetDuck.Core.Notification{
|
|||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.owner.FormClosed += owner_FormClosed;
|
this.owner.FormClosed += owner_FormClosed;
|
||||||
|
|
||||||
|
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||||
|
resourceHandlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
||||||
|
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo);
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser("about:blank"){
|
this.browser = new ChromiumWebBrowser("about:blank"){
|
||||||
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
MenuHandler = new ContextMenuNotification(this, enableContextMenu),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBase(false)
|
RequestHandler = new RequestHandlerBase(false),
|
||||||
|
ResourceHandlerFactory = resourceHandlerFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
this.browser.ClientSize = ClientSize;
|
this.browser.ClientSize = ClientSize;
|
||||||
|
this.browser.SetupZoomEvents();
|
||||||
browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
|
|
||||||
browser.SetupResourceHandler(TweetNotification.AppLogo);
|
|
||||||
|
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
|
|
||||||
@@ -184,9 +188,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual string GetTweetHTML(TweetNotification tweet){
|
protected abstract string GetTweetHTML(TweetNotification tweet);
|
||||||
return tweet.GenerateHtml(IsCursorOverBrowser ? "td-notification td-hover" : "td-notification", this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void LoadTweet(TweetNotification tweet){
|
protected virtual void LoadTweet(TweetNotification tweet){
|
||||||
currentNotification = tweet;
|
currentNotification = tweet;
|
||||||
@@ -202,7 +204,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
protected virtual void UpdateTitle(){
|
protected virtual void UpdateTitle(){
|
||||||
string title = currentNotification?.ColumnTitle;
|
string title = currentNotification?.ColumnTitle;
|
||||||
Text = string.IsNullOrEmpty(title) || !Program.UserConfig.DisplayNotificationColumn ? Program.BrandName : Program.BrandName+" - "+title;
|
Text = string.IsNullOrEmpty(title) || !Config.DisplayNotificationColumn ? Program.BrandName : $"{Program.BrandName} - {title}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ShowTweetDetail(){
|
public void ShowTweetDetail(){
|
||||||
|
@@ -5,7 +5,6 @@ using System.Windows.Forms;
|
|||||||
using TweetDuck.Core.Bridge;
|
using TweetDuck.Core.Bridge;
|
||||||
using TweetDuck.Core.Controls;
|
using TweetDuck.Core.Controls;
|
||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Other.Interfaces;
|
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
@@ -13,7 +12,7 @@ using TweetDuck.Plugins.Enums;
|
|||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Notification{
|
namespace TweetDuck.Core.Notification{
|
||||||
abstract partial class FormNotificationMain : FormNotificationBase, ITweetDeckBrowser{
|
abstract partial class FormNotificationMain : FormNotificationBase{
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private readonly int timerBarHeight;
|
private readonly int timerBarHeight;
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
public virtual bool RequiresResize{
|
public virtual bool RequiresResize{
|
||||||
get{
|
get{
|
||||||
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
|
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Config.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
set{
|
set{
|
||||||
@@ -38,7 +37,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
prevFontSize = null;
|
prevFontSize = null;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
|
prevDisplayTimer = Config.DisplayNotificationTimer;
|
||||||
prevFontSize = FontSizeLevel;
|
prevFontSize = FontSizeLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,29 +45,31 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private int BaseClientWidth{
|
private int BaseClientWidth{
|
||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Config.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel));
|
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel));
|
||||||
|
|
||||||
case TweetNotification.Size.Custom:
|
case TweetNotification.Size.Custom:
|
||||||
return Program.UserConfig.CustomNotificationSize.Width;
|
return Config.CustomNotificationSize.Width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int BaseClientHeight{
|
private int BaseClientHeight{
|
||||||
get{
|
get{
|
||||||
switch(Program.UserConfig.NotificationSize){
|
switch(Config.NotificationSize){
|
||||||
default:
|
default:
|
||||||
return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel));
|
return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel));
|
||||||
|
|
||||||
case TweetNotification.Size.Custom:
|
case TweetNotification.Size.Custom:
|
||||||
return Program.UserConfig.CustomNotificationSize.Height;
|
return Config.CustomNotificationSize.Height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Size BrowserSize => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize;
|
protected virtual string BodyClasses => IsCursorOverBrowser ? "td-notification td-hover" : "td-notification";
|
||||||
|
|
||||||
|
public Size BrowserSize => Config.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize;
|
||||||
|
|
||||||
protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -82,34 +83,12 @@ namespace TweetDuck.Core.Notification{
|
|||||||
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
browser.LoadingStateChanged += Browser_LoadingStateChanged;
|
||||||
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
||||||
|
|
||||||
plugins.Register(this, PluginEnvironment.Notification, this);
|
plugins.Register(browser, PluginEnvironment.Notification, this);
|
||||||
|
|
||||||
mouseHookDelegate = MouseHookProc;
|
mouseHookDelegate = MouseHookProc;
|
||||||
Disposed += (sender, args) => StopMouseHook(true);
|
Disposed += (sender, args) => StopMouseHook(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// implementation of ITweetDeckBrowser
|
|
||||||
|
|
||||||
bool ITweetDeckBrowser.IsTweetDeckWebsite => IsNotificationVisible;
|
|
||||||
|
|
||||||
void ITweetDeckBrowser.RegisterBridge(string name, object obj){
|
|
||||||
browser.RegisterAsyncJsObject(name, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){
|
|
||||||
browser.FrameLoadEnd += (sender, args) => {
|
|
||||||
IFrame frame = args.Frame;
|
|
||||||
|
|
||||||
if (frame.IsMain && browser.Address != "about:blank"){
|
|
||||||
callback(frame);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){
|
|
||||||
browser.ExecuteScriptAsync(name, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mouse wheel hook
|
// mouse wheel hook
|
||||||
|
|
||||||
private void StartMouseHook(){
|
private void StartMouseHook(){
|
||||||
@@ -131,7 +110,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
int eventType = wParam.ToInt32();
|
int eventType = wParam.ToInt32();
|
||||||
|
|
||||||
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
|
||||||
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
|
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Config.NotificationScrollSpeed*0.01), CefEventFlags.None);
|
||||||
return NativeMethods.HOOK_HANDLED;
|
return NativeMethods.HOOK_HANDLED;
|
||||||
}
|
}
|
||||||
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
|
||||||
@@ -181,9 +160,11 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
if (e.Frame.IsMain && browser.Address != "about:blank"){
|
IFrame frame = e.Frame;
|
||||||
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
|
||||||
ScriptLoader.ExecuteScript(e.Frame, ScriptLoader.LoadResource("notification.js", this), "root:notification");
|
if (frame.IsMain && browser.Address != "about:blank"){
|
||||||
|
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
|
||||||
|
ScriptLoader.ExecuteFile(frame, "notification.js", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +181,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
timeLeft -= timerProgress.Interval;
|
timeLeft -= timerProgress.Interval;
|
||||||
|
|
||||||
int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime);
|
int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime);
|
||||||
progressBarTimer.SetValueInstant(Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value);
|
progressBarTimer.SetValueInstant(Config.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value);
|
||||||
|
|
||||||
if (timeLeft <= 0){
|
if (timeLeft <= 0){
|
||||||
FinishCurrentNotification();
|
FinishCurrentNotification();
|
||||||
@@ -216,7 +197,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
public override void HideNotification(){
|
public override void HideNotification(){
|
||||||
base.HideNotification();
|
base.HideNotification();
|
||||||
|
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = 0;
|
totalTime = 0;
|
||||||
|
|
||||||
@@ -247,7 +228,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetTweetHTML(TweetNotification tweet){
|
protected override string GetTweetHTML(TweetNotification tweet){
|
||||||
string html = base.GetTweetHTML(tweet);
|
string html = tweet.GenerateHtml(BodyClasses, this);
|
||||||
|
|
||||||
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
foreach(InjectedHTML injection in plugins.NotificationInjections){
|
||||||
html = injection.InjectInto(html);
|
html = injection.InjectInto(html);
|
||||||
@@ -258,14 +239,14 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
protected override void LoadTweet(TweetNotification tweet){
|
protected override void LoadTweet(TweetNotification tweet){
|
||||||
timerProgress.Stop();
|
timerProgress.Stop();
|
||||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
totalTime = timeLeft = tweet.GetDisplayDuration(Config.NotificationDurationValue);
|
||||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
|
||||||
|
|
||||||
base.LoadTweet(tweet);
|
base.LoadTweet(tweet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetNotificationSize(int width, int height){
|
protected override void SetNotificationSize(int width, int height){
|
||||||
if (Program.UserConfig.DisplayNotificationTimer){
|
if (Config.DisplayNotificationTimer){
|
||||||
ClientSize = new Size(width, height+timerBarHeight);
|
ClientSize = new Size(width, height+timerBarHeight);
|
||||||
progressBarTimer.Visible = true;
|
progressBarTimer.Visible = true;
|
||||||
}
|
}
|
||||||
|
@@ -32,10 +32,10 @@ namespace TweetDuck.Core.Notification{
|
|||||||
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Program.UserConfig.MuteToggled += Config_MuteToggled;
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
|
Disposed += (sender, args) => Config.MuteToggled -= Config_MuteToggled;
|
||||||
|
|
||||||
if (Program.UserConfig.MuteNotifications){
|
if (Config.MuteNotifications){
|
||||||
PauseNotification();
|
PauseNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
// event handlers
|
// event handlers
|
||||||
|
|
||||||
private void Config_MuteToggled(object sender, EventArgs e){
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
if (Program.UserConfig.MuteNotifications){
|
if (Config.MuteNotifications){
|
||||||
PauseNotification();
|
PauseNotification();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
@@ -73,7 +73,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void timerIdlePauseCheck_Tick(object sender, EventArgs e){
|
private void timerIdlePauseCheck_Tick(object sender, EventArgs e){
|
||||||
if (NativeMethods.GetIdleSeconds() < Program.UserConfig.NotificationIdlePauseSeconds){
|
if (NativeMethods.GetIdleSeconds() < Config.NotificationIdlePauseSeconds){
|
||||||
ResumeNotification();
|
ResumeNotification();
|
||||||
timerIdlePauseCheck.Stop();
|
timerIdlePauseCheck.Stop();
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private void LoadNextNotification(){
|
private void LoadNextNotification(){
|
||||||
if (!IsNotificationVisible){
|
if (!IsNotificationVisible){
|
||||||
if (Program.UserConfig.NotificationNonIntrusiveMode && IsCursorOverNotificationArea && NativeMethods.GetIdleSeconds() < NonIntrusiveIdleLimit){
|
if (Config.NotificationNonIntrusiveMode && IsCursorOverNotificationArea && NativeMethods.GetIdleSeconds() < NonIntrusiveIdleLimit){
|
||||||
if (!timerCursorCheck.Enabled){
|
if (!timerCursorCheck.Enabled){
|
||||||
PauseNotification();
|
PauseNotification();
|
||||||
timerCursorCheck.Start();
|
timerCursorCheck.Start();
|
||||||
@@ -136,7 +136,7 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (Program.UserConfig.NotificationIdlePauseSeconds > 0 && NativeMethods.GetIdleSeconds() >= Program.UserConfig.NotificationIdlePauseSeconds){
|
else if (Config.NotificationIdlePauseSeconds > 0 && NativeMethods.GetIdleSeconds() >= Config.NotificationIdlePauseSeconds){
|
||||||
if (!timerIdlePauseCheck.Enabled){
|
if (!timerIdlePauseCheck.Enabled){
|
||||||
PauseNotification();
|
PauseNotification();
|
||||||
timerIdlePauseCheck.Start();
|
timerIdlePauseCheck.Start();
|
||||||
|
@@ -11,10 +11,6 @@ namespace TweetDuck.Core.Notification{
|
|||||||
private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
|
private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
|
||||||
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
|
||||||
|
|
||||||
public static TweetNotification Example(string html, int characters){
|
|
||||||
return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Position{
|
public enum Position{
|
||||||
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
TopLeft, TopRight, BottomLeft, BottomRight, Custom
|
||||||
}
|
}
|
||||||
@@ -32,11 +28,8 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
private readonly string html;
|
private readonly string html;
|
||||||
private readonly int characters;
|
private readonly int characters;
|
||||||
private readonly bool isExample;
|
|
||||||
|
|
||||||
public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl) : this(columnId, chirpId, title, html, characters, tweetUrl, quoteUrl, false){}
|
public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl){
|
||||||
|
|
||||||
private TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl, bool isExample){
|
|
||||||
this.ColumnId = columnId;
|
this.ColumnId = columnId;
|
||||||
this.ChirpId = chirpId;
|
this.ChirpId = chirpId;
|
||||||
|
|
||||||
@@ -46,7 +39,6 @@ namespace TweetDuck.Core.Notification{
|
|||||||
|
|
||||||
this.html = html;
|
this.html = html;
|
||||||
this.characters = characters;
|
this.characters = characters;
|
||||||
this.isExample = isExample;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDisplayDuration(int value){
|
public int GetDisplayDuration(int value){
|
||||||
@@ -54,26 +46,28 @@ namespace TweetDuck.Core.Notification{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateHtml(string bodyClasses, Control sync){
|
public string GenerateHtml(string bodyClasses, Control sync){
|
||||||
StringBuilder build = new StringBuilder();
|
string headLayout = TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout;
|
||||||
build.Append("<!DOCTYPE html>");
|
string mainCSS = ScriptLoader.LoadResource("styles/notification.css", sync) ?? string.Empty;
|
||||||
build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
|
string customCSS = Program.Config.User.CustomNotificationCSS ?? string.Empty;
|
||||||
build.Append("<style type='text/css'>").Append(ScriptLoader.LoadResource("styles/notification.css", sync) ?? string.Empty).Append("</style>");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Program.UserConfig.CustomNotificationCSS)){
|
StringBuilder build = new StringBuilder(320 + headLayout.Length + mainCSS.Length + customCSS.Length + html.Length);
|
||||||
build.Append("<style type='text/css'>").Append(Program.UserConfig.CustomNotificationCSS).Append("</style>");
|
build.Append("<!DOCTYPE html>");
|
||||||
|
build.Append(headLayout);
|
||||||
|
build.Append("<style type='text/css'>").Append(mainCSS).Append("</style>");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(customCSS)){
|
||||||
|
build.Append("<style type='text/css'>").Append(customCSS).Append("</style>");
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append("</head>");
|
build.Append("</head><body class='scroll-styled-v");
|
||||||
build.Append("<body class='scroll-styled-v");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(bodyClasses)){
|
if (!string.IsNullOrEmpty(bodyClasses)){
|
||||||
build.Append(' ').Append(bodyClasses);
|
build.Append(' ').Append(bodyClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>");
|
build.Append("'><div class='column' style='width:100%!important;min-height:100vh!important;height:auto!important;overflow:initial!important;'>");
|
||||||
build.Append(html);
|
build.Append(html);
|
||||||
build.Append("</div></body>");
|
build.Append("</div></body></html>");
|
||||||
build.Append("</html>");
|
|
||||||
return build.ToString();
|
return build.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,7 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString(){
|
public override string ToString(){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder(625);
|
||||||
|
|
||||||
foreach(DictionaryEntry entry in data){
|
foreach(DictionaryEntry entry in data){
|
||||||
if (entry.Value == null){
|
if (entry.Value == null){
|
||||||
|
@@ -119,8 +119,8 @@ namespace TweetDuck.Core.Other.Analytics{
|
|||||||
}.FinalizeReport();
|
}.FinalizeReport();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UserConfig UserConfig => Program.UserConfig;
|
private static UserConfig UserConfig => Program.Config.User;
|
||||||
private static SystemConfig SysConfig => Program.SystemConfig;
|
private static SystemConfig SysConfig => Program.Config.System;
|
||||||
|
|
||||||
private static string Bool(bool value) => value ? "on" : "off";
|
private static string Bool(bool value) => value ? "on" : "off";
|
||||||
private static string Exact(int value) => value.ToString();
|
private static string Exact(int value) => value.ToString();
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Drawing;
|
||||||
using System.Drawing;
|
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
@@ -65,31 +64,31 @@ namespace TweetDuck.Core.Other{
|
|||||||
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
|
||||||
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
VisibleChanged += (sender, args) => this.MoveToCenter(owner);
|
||||||
|
|
||||||
|
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||||
|
resourceHandlerFactory.RegisterHandler(DummyPage);
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(url){
|
this.browser = new ChromiumWebBrowser(url){
|
||||||
MenuHandler = new ContextMenuGuide(owner),
|
MenuHandler = new ContextMenuGuide(owner),
|
||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
|
KeyboardHandler = new KeyboardHandlerBase(),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = new RequestHandlerBase(true)
|
RequestHandler = new RequestHandlerBase(true),
|
||||||
|
ResourceHandlerFactory = resourceHandlerFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
browser.FrameLoadStart += browser_FrameLoadStart;
|
|
||||||
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
browser.FrameLoadEnd += browser_FrameLoadEnd;
|
||||||
|
|
||||||
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
|
browser.BrowserSettings.BackgroundColor = (uint)BackColor.ToArgb();
|
||||||
browser.Dock = DockStyle.None;
|
browser.Dock = DockStyle.None;
|
||||||
browser.Location = ControlExtensions.InvisibleLocation;
|
browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
browser.SetupZoomEvents();
|
||||||
browser.SetupResourceHandler(DummyPage);
|
|
||||||
|
|
||||||
Controls.Add(browser);
|
Controls.Add(browser);
|
||||||
|
|
||||||
Disposed += (sender, args) => {
|
Disposed += (sender, args) => {
|
||||||
Program.UserConfig.ZoomLevelChanged -= Config_ZoomLevelChanged;
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Reload(string url){
|
private void Reload(string url){
|
||||||
@@ -116,16 +115,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
|
||||||
ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links");
|
ScriptLoader.ExecuteScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Config_ZoomLevelChanged(object sender, EventArgs e){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,14 @@ using System.Diagnostics;
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Plugins;
|
using TweetDuck.Plugins;
|
||||||
using TweetDuck.Plugins.Controls;
|
using TweetDuck.Plugins.Controls;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other{
|
namespace TweetDuck.Core.Other{
|
||||||
sealed partial class FormPlugins : Form, FormManager.IAppDialog{
|
sealed partial class FormPlugins : Form, FormManager.IAppDialog{
|
||||||
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
private readonly PluginManager pluginManager;
|
private readonly PluginManager pluginManager;
|
||||||
|
|
||||||
public FormPlugins(){
|
public FormPlugins(){
|
||||||
@@ -19,8 +22,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
public FormPlugins(PluginManager pluginManager) : this(){
|
public FormPlugins(PluginManager pluginManager) : this(){
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
|
|
||||||
if (!Program.UserConfig.PluginsWindowSize.IsEmpty){
|
if (!Config.PluginsWindowSize.IsEmpty){
|
||||||
Size targetSize = Program.UserConfig.PluginsWindowSize;
|
Size targetSize = Config.PluginsWindowSize;
|
||||||
Size = new Size(Math.Max(MinimumSize.Width, targetSize.Width), Math.Max(MinimumSize.Height, targetSize.Height));
|
Size = new Size(Math.Max(MinimumSize.Width, targetSize.Width), Math.Max(MinimumSize.Height, targetSize.Height));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,8 +32,8 @@ namespace TweetDuck.Core.Other{
|
|||||||
};
|
};
|
||||||
|
|
||||||
FormClosed += (sender, args) => {
|
FormClosed += (sender, args) => {
|
||||||
Program.UserConfig.PluginsWindowSize = Size;
|
Config.PluginsWindowSize = Size;
|
||||||
Program.UserConfig.Save();
|
Config.Save();
|
||||||
};
|
};
|
||||||
|
|
||||||
ResizeEnd += (sender, args) => {
|
ResizeEnd += (sender, args) => {
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using CefSharp;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Other.Interfaces{
|
|
||||||
interface ITweetDeckBrowser{
|
|
||||||
bool IsTweetDeckWebsite { get; }
|
|
||||||
|
|
||||||
void RegisterBridge(string name, object obj);
|
|
||||||
void OnFrameLoaded(Action<IFrame> callback);
|
|
||||||
void ExecuteFunction(string name, params object[] args);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,8 +4,8 @@ using TweetDuck.Configuration;
|
|||||||
|
|
||||||
namespace TweetDuck.Core.Other.Settings{
|
namespace TweetDuck.Core.Other.Settings{
|
||||||
class BaseTabSettings : UserControl{
|
class BaseTabSettings : UserControl{
|
||||||
protected static UserConfig Config => Program.UserConfig;
|
protected static UserConfig Config => Program.Config.User;
|
||||||
protected static SystemConfig SysConfig => Program.SystemConfig;
|
protected static SystemConfig SysConfig => Program.Config.System;
|
||||||
|
|
||||||
public IEnumerable<Control> InteractiveControls{
|
public IEnumerable<Control> InteractiveControls{
|
||||||
get{
|
get{
|
||||||
|
@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
private readonly Action<string> reinjectBrowserCSS;
|
private readonly Action<string> reinjectBrowserCSS;
|
||||||
private readonly Action openDevTools;
|
private readonly Action openDevTools;
|
||||||
|
|
||||||
public DialogSettingsCSS(Action<string> reinjectBrowserCSS, Action openDevTools){
|
public DialogSettingsCSS(string browserCSS, string notificationCSS, Action<string> reinjectBrowserCSS, Action openDevTools){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Options - CSS";
|
Text = Program.BrandName+" Options - CSS";
|
||||||
@@ -22,10 +22,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
this.openDevTools = openDevTools;
|
this.openDevTools = openDevTools;
|
||||||
|
|
||||||
textBoxBrowserCSS.EnableMultilineShortcuts();
|
textBoxBrowserCSS.EnableMultilineShortcuts();
|
||||||
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
|
textBoxBrowserCSS.Text = browserCSS ?? "";
|
||||||
|
|
||||||
textBoxNotificationCSS.EnableMultilineShortcuts();
|
textBoxNotificationCSS.EnableMultilineShortcuts();
|
||||||
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
|
textBoxNotificationCSS.Text = notificationCSS ?? "";
|
||||||
|
|
||||||
if (!BrowserUtils.HasDevTools){
|
if (!BrowserUtils.HasDevTools){
|
||||||
btnOpenDevTools.Enabled = false;
|
btnOpenDevTools.Enabled = false;
|
||||||
|
@@ -8,13 +8,15 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
sealed partial class DialogSettingsCefArgs : Form{
|
sealed partial class DialogSettingsCefArgs : Form{
|
||||||
public string CefArgs => textBoxArgs.Text;
|
public string CefArgs => textBoxArgs.Text;
|
||||||
|
|
||||||
public DialogSettingsCefArgs(){
|
private readonly string initialArgs;
|
||||||
|
|
||||||
|
public DialogSettingsCefArgs(string args){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Text = Program.BrandName+" Options - CEF Arguments";
|
Text = Program.BrandName+" Options - CEF Arguments";
|
||||||
|
|
||||||
textBoxArgs.EnableMultilineShortcuts();
|
textBoxArgs.EnableMultilineShortcuts();
|
||||||
textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? "";
|
textBoxArgs.Text = initialArgs = args ?? "";
|
||||||
textBoxArgs.Select(textBoxArgs.Text.Length, 0);
|
textBoxArgs.Select(textBoxArgs.Text.Length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,16 +25,14 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnApply_Click(object sender, EventArgs e){
|
private void btnApply_Click(object sender, EventArgs e){
|
||||||
string prevArgs = Program.UserConfig.CustomCefArgs;
|
if (CefArgs == initialArgs){
|
||||||
|
|
||||||
if (CefArgs == prevArgs){
|
|
||||||
DialogResult = DialogResult.Cancel;
|
DialogResult = DialogResult.Cancel;
|
||||||
Close();
|
Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
|
int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
|
||||||
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
|
string prompt = count == 0 && !string.IsNullOrWhiteSpace(initialArgs) ? "All current arguments will be removed. Continue?" : count+(count == 1 ? " argument was" : " arguments were")+" detected. Continue?";
|
||||||
|
|
||||||
if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){
|
if (FormMessage.Question("Confirm CEF Arguments", prompt, FormMessage.OK, FormMessage.Cancel)){
|
||||||
DialogResult = DialogResult.OK;
|
DialogResult = DialogResult.OK;
|
||||||
|
@@ -33,6 +33,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
|
|
||||||
private readonly PluginManager plugins;
|
private readonly PluginManager plugins;
|
||||||
private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4);
|
private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4);
|
||||||
|
private readonly bool openImportImmediately;
|
||||||
|
|
||||||
private State currentState;
|
private State currentState;
|
||||||
private ProfileManager importManager;
|
private ProfileManager importManager;
|
||||||
@@ -51,6 +52,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
this.checkBoxMap[cbSession] = ProfileManager.Items.Session;
|
this.checkBoxMap[cbSession] = ProfileManager.Items.Session;
|
||||||
this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData;
|
this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData;
|
||||||
|
|
||||||
|
this.openImportImmediately = openImportImmediately;
|
||||||
|
|
||||||
if (openImportImmediately){
|
if (openImportImmediately){
|
||||||
radioImport.Checked = true;
|
radioImport.Checked = true;
|
||||||
btnContinue_Click(null, EventArgs.Empty);
|
btnContinue_Click(null, EventArgs.Empty);
|
||||||
@@ -88,6 +91,10 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
|
Filter = "TweetDuck Profile (*.tdsettings)|*.tdsettings"
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() != DialogResult.OK){
|
if (dialog.ShowDialog() != DialogResult.OK){
|
||||||
|
if (openImportImmediately){
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,18 +132,19 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested;
|
Program.Config.ProgramRestartRequested += Config_ProgramRestartRequested;
|
||||||
|
|
||||||
if (SelectedItems.HasFlag(ProfileManager.Items.UserConfig)){
|
if (SelectedItems.HasFlag(ProfileManager.Items.UserConfig)){
|
||||||
Program.UserConfig.Reset();
|
Program.Config.User.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){
|
if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){
|
||||||
Program.SystemConfig.Reset();
|
Program.Config.System.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested;
|
Program.Config.ProgramRestartRequested -= Config_ProgramRestartRequested;
|
||||||
|
|
||||||
if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)){
|
if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)){
|
||||||
|
Program.Config.Plugins.Reset();
|
||||||
|
|
||||||
try{
|
try{
|
||||||
File.Delete(Program.PluginConfigFilePath);
|
|
||||||
Directory.Delete(Program.PluginDataPath, true);
|
Directory.Delete(Program.PluginDataPath, true);
|
||||||
}catch(Exception ex){
|
}catch(Exception ex){
|
||||||
Program.Reporter.HandleException("Profile Reset", "Could not delete plugin data.", true, ex);
|
Program.Reporter.HandleException("Profile Reset", "Could not delete plugin data.", true, ex);
|
||||||
|
@@ -10,7 +10,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
|
|||||||
|
|
||||||
Text = Program.BrandName+" Options - Custom Search Engine";
|
Text = Program.BrandName+" Options - Custom Search Engine";
|
||||||
|
|
||||||
textBoxUrl.Text = Program.UserConfig.SearchEngineUrl ?? "";
|
textBoxUrl.Text = Program.Config.User.SearchEngineUrl ?? "";
|
||||||
textBoxUrl.Select(textBoxUrl.Text.Length, 0);
|
textBoxUrl.Select(textBoxUrl.Text.Length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -103,7 +103,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
#region Configuration
|
#region Configuration
|
||||||
|
|
||||||
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
private void btnEditCefArgs_Click(object sender, EventArgs e){
|
||||||
DialogSettingsCefArgs form = new DialogSettingsCefArgs();
|
DialogSettingsCefArgs form = new DialogSettingsCefArgs(Config.CustomCefArgs);
|
||||||
|
|
||||||
form.VisibleChanged += (sender2, args2) => {
|
form.VisibleChanged += (sender2, args2) => {
|
||||||
form.MoveToCenter(ParentForm);
|
form.MoveToCenter(ParentForm);
|
||||||
@@ -124,7 +124,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnEditCSS_Click(object sender, EventArgs e){
|
private void btnEditCSS_Click(object sender, EventArgs e){
|
||||||
DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS, openDevTools);
|
DialogSettingsCSS form = new DialogSettingsCSS(Config.CustomBrowserCSS, Config.CustomNotificationCSS, reinjectBrowserCSS, openDevTools);
|
||||||
|
|
||||||
form.VisibleChanged += (sender2, args2) => {
|
form.VisibleChanged += (sender2, args2) => {
|
||||||
form.MoveToCenter(ParentForm);
|
form.MoveToCenter(ParentForm);
|
||||||
|
71
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
71
Core/Other/Settings/TabSettingsGeneral.Designer.cs
generated
@@ -39,6 +39,7 @@
|
|||||||
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
|
this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
|
||||||
this.labelUpdates = new System.Windows.Forms.Label();
|
this.labelUpdates = new System.Windows.Forms.Label();
|
||||||
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
|
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
|
||||||
|
this.checkFocusDmInput = new System.Windows.Forms.CheckBox();
|
||||||
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
|
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
|
||||||
this.labelTray = new System.Windows.Forms.Label();
|
this.labelTray = new System.Windows.Forms.Label();
|
||||||
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
|
||||||
@@ -82,11 +83,11 @@
|
|||||||
//
|
//
|
||||||
this.checkUpdateNotifications.AutoSize = true;
|
this.checkUpdateNotifications.AutoSize = true;
|
||||||
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkUpdateNotifications.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 393);
|
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 409);
|
||||||
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||||
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
|
||||||
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
|
this.checkUpdateNotifications.Size = new System.Drawing.Size(182, 19);
|
||||||
this.checkUpdateNotifications.TabIndex = 13;
|
this.checkUpdateNotifications.TabIndex = 14;
|
||||||
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
this.checkUpdateNotifications.Text = "Check Updates Automatically";
|
||||||
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -94,12 +95,12 @@
|
|||||||
//
|
//
|
||||||
this.btnCheckUpdates.AutoSize = true;
|
this.btnCheckUpdates.AutoSize = true;
|
||||||
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.btnCheckUpdates.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 417);
|
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 433);
|
||||||
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
this.btnCheckUpdates.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
|
||||||
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
this.btnCheckUpdates.Name = "btnCheckUpdates";
|
||||||
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.btnCheckUpdates.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
|
this.btnCheckUpdates.Size = new System.Drawing.Size(128, 25);
|
||||||
this.btnCheckUpdates.TabIndex = 14;
|
this.btnCheckUpdates.TabIndex = 15;
|
||||||
this.btnCheckUpdates.Text = "Check Updates Now";
|
this.btnCheckUpdates.Text = "Check Updates Now";
|
||||||
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
this.btnCheckUpdates.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -119,11 +120,11 @@
|
|||||||
//
|
//
|
||||||
this.checkBestImageQuality.AutoSize = true;
|
this.checkBestImageQuality.AutoSize = true;
|
||||||
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkBestImageQuality.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 98);
|
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 122);
|
||||||
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
this.checkBestImageQuality.Name = "checkBestImageQuality";
|
||||||
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
|
this.checkBestImageQuality.Size = new System.Drawing.Size(125, 19);
|
||||||
this.checkBestImageQuality.TabIndex = 4;
|
this.checkBestImageQuality.TabIndex = 5;
|
||||||
this.checkBestImageQuality.Text = "Best Image Quality";
|
this.checkBestImageQuality.Text = "Best Image Quality";
|
||||||
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
this.checkBestImageQuality.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -131,11 +132,11 @@
|
|||||||
//
|
//
|
||||||
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
this.checkOpenSearchInFirstColumn.AutoSize = true;
|
||||||
this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkOpenSearchInFirstColumn.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 50);
|
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 74);
|
||||||
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
|
||||||
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19);
|
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(245, 19);
|
||||||
this.checkOpenSearchInFirstColumn.TabIndex = 2;
|
this.checkOpenSearchInFirstColumn.TabIndex = 3;
|
||||||
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
|
||||||
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -158,11 +159,11 @@
|
|||||||
//
|
//
|
||||||
this.labelZoom.AutoSize = true;
|
this.labelZoom.AutoSize = true;
|
||||||
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
this.labelZoom.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelZoom.Location = new System.Drawing.Point(3, 155);
|
this.labelZoom.Location = new System.Drawing.Point(3, 179);
|
||||||
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
this.labelZoom.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
|
||||||
this.labelZoom.Name = "labelZoom";
|
this.labelZoom.Name = "labelZoom";
|
||||||
this.labelZoom.Size = new System.Drawing.Size(39, 15);
|
this.labelZoom.Size = new System.Drawing.Size(39, 15);
|
||||||
this.labelZoom.TabIndex = 6;
|
this.labelZoom.TabIndex = 7;
|
||||||
this.labelZoom.Text = "Zoom";
|
this.labelZoom.Text = "Zoom";
|
||||||
//
|
//
|
||||||
// zoomUpdateTimer
|
// zoomUpdateTimer
|
||||||
@@ -185,21 +186,21 @@
|
|||||||
//
|
//
|
||||||
this.panelZoom.Controls.Add(this.trackBarZoom);
|
this.panelZoom.Controls.Add(this.trackBarZoom);
|
||||||
this.panelZoom.Controls.Add(this.labelZoomValue);
|
this.panelZoom.Controls.Add(this.labelZoomValue);
|
||||||
this.panelZoom.Location = new System.Drawing.Point(0, 171);
|
this.panelZoom.Location = new System.Drawing.Point(0, 195);
|
||||||
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
|
this.panelZoom.Margin = new System.Windows.Forms.Padding(0, 1, 0, 0);
|
||||||
this.panelZoom.Name = "panelZoom";
|
this.panelZoom.Name = "panelZoom";
|
||||||
this.panelZoom.Size = new System.Drawing.Size(300, 35);
|
this.panelZoom.Size = new System.Drawing.Size(300, 35);
|
||||||
this.panelZoom.TabIndex = 7;
|
this.panelZoom.TabIndex = 8;
|
||||||
//
|
//
|
||||||
// checkAnimatedAvatars
|
// checkAnimatedAvatars
|
||||||
//
|
//
|
||||||
this.checkAnimatedAvatars.AutoSize = true;
|
this.checkAnimatedAvatars.AutoSize = true;
|
||||||
this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkAnimatedAvatars.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 122);
|
this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 146);
|
||||||
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
|
||||||
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
|
this.checkAnimatedAvatars.Size = new System.Drawing.Size(158, 19);
|
||||||
this.checkAnimatedAvatars.TabIndex = 5;
|
this.checkAnimatedAvatars.TabIndex = 6;
|
||||||
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
|
||||||
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -207,11 +208,11 @@
|
|||||||
//
|
//
|
||||||
this.labelUpdates.AutoSize = true;
|
this.labelUpdates.AutoSize = true;
|
||||||
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
this.labelUpdates.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelUpdates.Location = new System.Drawing.Point(0, 367);
|
this.labelUpdates.Location = new System.Drawing.Point(0, 383);
|
||||||
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1);
|
||||||
this.labelUpdates.Name = "labelUpdates";
|
this.labelUpdates.Name = "labelUpdates";
|
||||||
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
|
this.labelUpdates.Size = new System.Drawing.Size(69, 19);
|
||||||
this.labelUpdates.TabIndex = 12;
|
this.labelUpdates.TabIndex = 13;
|
||||||
this.labelUpdates.Text = "UPDATES";
|
this.labelUpdates.Text = "UPDATES";
|
||||||
//
|
//
|
||||||
// flowPanelLeft
|
// flowPanelLeft
|
||||||
@@ -220,6 +221,7 @@
|
|||||||
| System.Windows.Forms.AnchorStyles.Left)));
|
| System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.flowPanelLeft.Controls.Add(this.labelUI);
|
this.flowPanelLeft.Controls.Add(this.labelUI);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkExpandLinks);
|
this.flowPanelLeft.Controls.Add(this.checkExpandLinks);
|
||||||
|
this.flowPanelLeft.Controls.Add(this.checkFocusDmInput);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
|
this.flowPanelLeft.Controls.Add(this.checkOpenSearchInFirstColumn);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
|
||||||
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
|
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
|
||||||
@@ -240,15 +242,27 @@
|
|||||||
this.flowPanelLeft.TabIndex = 0;
|
this.flowPanelLeft.TabIndex = 0;
|
||||||
this.flowPanelLeft.WrapContents = false;
|
this.flowPanelLeft.WrapContents = false;
|
||||||
//
|
//
|
||||||
|
// checkFocusDmInput
|
||||||
|
//
|
||||||
|
this.checkFocusDmInput.AutoSize = true;
|
||||||
|
this.checkFocusDmInput.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
|
this.checkFocusDmInput.Location = new System.Drawing.Point(6, 50);
|
||||||
|
this.checkFocusDmInput.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
|
this.checkFocusDmInput.Name = "checkFocusDmInput";
|
||||||
|
this.checkFocusDmInput.Size = new System.Drawing.Size(282, 19);
|
||||||
|
this.checkFocusDmInput.TabIndex = 2;
|
||||||
|
this.checkFocusDmInput.Text = "Focus Input Field When Opening Direct Message";
|
||||||
|
this.checkFocusDmInput.UseVisualStyleBackColor = true;
|
||||||
|
//
|
||||||
// checkKeepLikeFollowDialogsOpen
|
// checkKeepLikeFollowDialogsOpen
|
||||||
//
|
//
|
||||||
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
this.checkKeepLikeFollowDialogsOpen.AutoSize = true;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkKeepLikeFollowDialogsOpen.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 74);
|
this.checkKeepLikeFollowDialogsOpen.Location = new System.Drawing.Point(6, 98);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
|
||||||
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
|
||||||
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19);
|
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(190, 19);
|
||||||
this.checkKeepLikeFollowDialogsOpen.TabIndex = 3;
|
this.checkKeepLikeFollowDialogsOpen.TabIndex = 4;
|
||||||
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
|
||||||
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -256,11 +270,11 @@
|
|||||||
//
|
//
|
||||||
this.labelTray.AutoSize = true;
|
this.labelTray.AutoSize = true;
|
||||||
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelTray.Location = new System.Drawing.Point(0, 236);
|
this.labelTray.Location = new System.Drawing.Point(0, 255);
|
||||||
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
|
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
|
||||||
this.labelTray.Name = "labelTray";
|
this.labelTray.Name = "labelTray";
|
||||||
this.labelTray.Size = new System.Drawing.Size(99, 19);
|
this.labelTray.Size = new System.Drawing.Size(99, 19);
|
||||||
this.labelTray.TabIndex = 8;
|
this.labelTray.TabIndex = 9;
|
||||||
this.labelTray.Text = "SYSTEM TRAY";
|
this.labelTray.Text = "SYSTEM TRAY";
|
||||||
//
|
//
|
||||||
// comboBoxTrayType
|
// comboBoxTrayType
|
||||||
@@ -268,32 +282,32 @@
|
|||||||
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||||
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.comboBoxTrayType.FormattingEnabled = true;
|
this.comboBoxTrayType.FormattingEnabled = true;
|
||||||
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 260);
|
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 279);
|
||||||
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
|
||||||
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
this.comboBoxTrayType.Name = "comboBoxTrayType";
|
||||||
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
|
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
|
||||||
this.comboBoxTrayType.TabIndex = 9;
|
this.comboBoxTrayType.TabIndex = 10;
|
||||||
//
|
//
|
||||||
// labelTrayIcon
|
// labelTrayIcon
|
||||||
//
|
//
|
||||||
this.labelTrayIcon.AutoSize = true;
|
this.labelTrayIcon.AutoSize = true;
|
||||||
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||||
this.labelTrayIcon.Location = new System.Drawing.Point(3, 295);
|
this.labelTrayIcon.Location = new System.Drawing.Point(3, 314);
|
||||||
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
|
||||||
this.labelTrayIcon.Name = "labelTrayIcon";
|
this.labelTrayIcon.Name = "labelTrayIcon";
|
||||||
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
|
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
|
||||||
this.labelTrayIcon.TabIndex = 10;
|
this.labelTrayIcon.TabIndex = 11;
|
||||||
this.labelTrayIcon.Text = "Tray Icon";
|
this.labelTrayIcon.Text = "Tray Icon";
|
||||||
//
|
//
|
||||||
// checkTrayHighlight
|
// checkTrayHighlight
|
||||||
//
|
//
|
||||||
this.checkTrayHighlight.AutoSize = true;
|
this.checkTrayHighlight.AutoSize = true;
|
||||||
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
|
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 316);
|
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 335);
|
||||||
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
|
||||||
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
this.checkTrayHighlight.Name = "checkTrayHighlight";
|
||||||
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
|
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
|
||||||
this.checkTrayHighlight.TabIndex = 11;
|
this.checkTrayHighlight.TabIndex = 12;
|
||||||
this.checkTrayHighlight.Text = "Enable Highlight";
|
this.checkTrayHighlight.Text = "Enable Highlight";
|
||||||
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
this.checkTrayHighlight.UseVisualStyleBackColor = true;
|
||||||
//
|
//
|
||||||
@@ -548,5 +562,6 @@
|
|||||||
private System.Windows.Forms.Label labelTranslationTarget;
|
private System.Windows.Forms.Label labelTranslationTarget;
|
||||||
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
|
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
|
||||||
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
|
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
|
||||||
|
private System.Windows.Forms.CheckBox checkFocusDmInput;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
// user interface
|
// user interface
|
||||||
|
|
||||||
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
|
||||||
|
toolTip.SetToolTip(checkFocusDmInput, "Places cursor into Direct Message input\r\nfield when opening a conversation.");
|
||||||
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
|
||||||
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
toolTip.SetToolTip(checkKeepLikeFollowDialogsOpen, "Allows liking and following from multiple accounts at once,\r\ninstead of automatically closing the dialog after taking an action.");
|
||||||
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
|
||||||
@@ -42,6 +43,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
|
||||||
|
|
||||||
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
|
||||||
|
checkFocusDmInput.Checked = Config.FocusDmInput;
|
||||||
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
|
||||||
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
checkKeepLikeFollowDialogsOpen.Checked = Config.KeepLikeFollowDialogsOpen;
|
||||||
checkBestImageQuality.Checked = Config.BestImageQuality;
|
checkBestImageQuality.Checked = Config.BestImageQuality;
|
||||||
@@ -127,6 +129,7 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
|
|
||||||
public override void OnReady(){
|
public override void OnReady(){
|
||||||
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
checkExpandLinks.CheckedChanged += checkExpandLinks_CheckedChanged;
|
||||||
|
checkFocusDmInput.CheckedChanged += checkFocusDmInput_CheckedChanged;
|
||||||
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
|
||||||
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
|
||||||
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
|
||||||
@@ -160,6 +163,10 @@ namespace TweetDuck.Core.Other.Settings{
|
|||||||
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkFocusDmInput_CheckedChanged(object sender, EventArgs e){
|
||||||
|
Config.FocusDmInput = checkFocusDmInput.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
private void checkOpenSearchInFirstColumn_CheckedChanged(object sender, EventArgs e){
|
||||||
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
Config.OpenSearchInFirstColumn = checkOpenSearchInFirstColumn.Checked;
|
||||||
}
|
}
|
||||||
|
49
Core/Other/TaskbarIcon.cs
Normal file
49
Core/Other/TaskbarIcon.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.WindowsAPICodePack.Taskbar;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
|
using Res = TweetDuck.Properties.Resources;
|
||||||
|
|
||||||
|
namespace TweetDuck.Core.Other{
|
||||||
|
sealed class TaskbarIcon : IDisposable{
|
||||||
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
|
public bool HasNotifications{
|
||||||
|
get{
|
||||||
|
return hasNotifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
set{
|
||||||
|
if (hasNotifications != value){
|
||||||
|
hasNotifications = value;
|
||||||
|
UpdateIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool hasNotifications;
|
||||||
|
|
||||||
|
public TaskbarIcon(){
|
||||||
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose(){
|
||||||
|
Config.MuteToggled -= Config_MuteToggled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
|
UpdateIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateIcon(){
|
||||||
|
if (hasNotifications){
|
||||||
|
TaskbarManager.Instance.SetOverlayIcon(Res.overlay_notification, "Unread Notifications");
|
||||||
|
}
|
||||||
|
else if (Config.MuteNotifications){
|
||||||
|
TaskbarManager.Instance.SetOverlayIcon(Res.overlay_muted, "Notifications Muted");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
TaskbarManager.Instance.SetOverlayIcon(null, string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,7 +10,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UserConfig Config => Program.UserConfig;
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
public event EventHandler ClickRestore;
|
public event EventHandler ClickRestore;
|
||||||
public event EventHandler ClickClose;
|
public event EventHandler ClickClose;
|
||||||
@@ -56,6 +56,7 @@ namespace TweetDuck.Core.Other{
|
|||||||
this.notifyIcon.Text = Program.BrandName;
|
this.notifyIcon.Text = Program.BrandName;
|
||||||
|
|
||||||
Config.MuteToggled += Config_MuteToggled;
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
|
Disposed += (sender, args) => Config.MuteToggled -= Config_MuteToggled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrayIcon(IContainer container) : this(){
|
public TrayIcon(IContainer container) : this(){
|
||||||
|
@@ -10,12 +10,18 @@ using TweetDuck.Core.Controls;
|
|||||||
using TweetDuck.Core.Handling;
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Core.Notification;
|
using TweetDuck.Core.Notification;
|
||||||
using TweetDuck.Core.Other.Interfaces;
|
|
||||||
using TweetDuck.Core.Utils;
|
using TweetDuck.Core.Utils;
|
||||||
|
using TweetDuck.Plugins;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Resources;
|
using TweetDuck.Resources;
|
||||||
|
|
||||||
namespace TweetDuck.Core{
|
namespace TweetDuck.Core{
|
||||||
sealed class TweetDeckBrowser : ITweetDeckBrowser, IDisposable{
|
sealed class TweetDeckBrowser : IDisposable{
|
||||||
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
|
||||||
|
private const string ErrorUrl = "http://td/error";
|
||||||
|
private const string TwitterStyleUrl = "https://abs.twimg.com/tduck/css";
|
||||||
|
|
||||||
public bool Ready { get; private set; }
|
public bool Ready { get; private set; }
|
||||||
|
|
||||||
public bool Enabled{
|
public bool Enabled{
|
||||||
@@ -36,10 +42,14 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly ChromiumWebBrowser browser;
|
private readonly ChromiumWebBrowser browser;
|
||||||
|
private readonly ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
|
||||||
|
|
||||||
private string prevSoundNotificationPath = null;
|
private string prevSoundNotificationPath = null;
|
||||||
|
|
||||||
public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
|
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
|
||||||
|
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo);
|
||||||
|
resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner);
|
||||||
|
|
||||||
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
|
||||||
|
|
||||||
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
|
||||||
@@ -49,7 +59,8 @@ namespace TweetDuck.Core{
|
|||||||
JsDialogHandler = new JavaScriptDialogHandler(),
|
JsDialogHandler = new JavaScriptDialogHandler(),
|
||||||
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
KeyboardHandler = new KeyboardHandlerBrowser(owner),
|
||||||
LifeSpanHandler = new LifeSpanHandler(),
|
LifeSpanHandler = new LifeSpanHandler(),
|
||||||
RequestHandler = requestHandler
|
RequestHandler = requestHandler,
|
||||||
|
ResourceHandlerFactory = resourceHandlerFactory
|
||||||
};
|
};
|
||||||
|
|
||||||
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
|
||||||
@@ -63,15 +74,13 @@ namespace TweetDuck.Core{
|
|||||||
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
|
||||||
this.browser.Dock = DockStyle.None;
|
this.browser.Dock = DockStyle.None;
|
||||||
this.browser.Location = ControlExtensions.InvisibleLocation;
|
this.browser.Location = ControlExtensions.InvisibleLocation;
|
||||||
|
this.browser.SetupZoomEvents();
|
||||||
this.browser.SetupResourceHandler(TweetNotification.AppLogo);
|
|
||||||
this.browser.SetupResourceHandler(TwitterUtils.LoadingSpinner);
|
|
||||||
|
|
||||||
owner.Controls.Add(browser);
|
owner.Controls.Add(browser);
|
||||||
|
plugins.Register(browser, PluginEnvironment.Browser, owner, true);
|
||||||
|
|
||||||
Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
|
Config.MuteToggled += Config_MuteToggled;
|
||||||
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
|
Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
|
||||||
Program.UserConfig.SoundNotificationChanged += UserConfig_SoundNotificationInfoChanged;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup and management
|
// setup and management
|
||||||
@@ -89,31 +98,12 @@ namespace TweetDuck.Core{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose(){
|
public void Dispose(){
|
||||||
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
|
Config.MuteToggled -= Config_MuteToggled;
|
||||||
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
|
Config.SoundNotificationChanged -= Config_SoundNotificationInfoChanged;
|
||||||
Program.UserConfig.SoundNotificationChanged -= UserConfig_SoundNotificationInfoChanged;
|
|
||||||
|
|
||||||
browser.Dispose();
|
browser.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ITweetDeckBrowser.RegisterBridge(string name, object obj){
|
|
||||||
browser.RegisterAsyncJsObject(name, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){
|
|
||||||
browser.FrameLoadEnd += (sender, args) => {
|
|
||||||
IFrame frame = args.Frame;
|
|
||||||
|
|
||||||
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
|
|
||||||
callback(frame);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){
|
|
||||||
browser.ExecuteScriptAsync(name, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
|
|
||||||
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
|
||||||
@@ -131,15 +121,16 @@ namespace TweetDuck.Core{
|
|||||||
IFrame frame = e.Frame;
|
IFrame frame = e.Frame;
|
||||||
|
|
||||||
if (frame.IsMain){
|
if (frame.IsMain){
|
||||||
if (Program.UserConfig.ZoomLevel != 100){
|
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TwitterUtils.IsTwitterWebsite(frame)){
|
if (TwitterUtils.IsTwitterWebsite(frame)){
|
||||||
|
string css = ScriptLoader.LoadResource("styles/twitter.css", browser);
|
||||||
|
resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css"));
|
||||||
|
|
||||||
ScriptLoader.ExecuteFile(frame, "twitter.js", browser);
|
ScriptLoader.ExecuteFile(frame, "twitter.js", browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
|
if (!TwitterUtils.IsTwitterLogin2FactorWebsite(frame)){
|
||||||
|
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +140,11 @@ namespace TweetDuck.Core{
|
|||||||
if (frame.IsMain){
|
if (frame.IsMain){
|
||||||
if (TwitterUtils.IsTweetDeckWebsite(frame)){
|
if (TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||||
UpdateProperties();
|
UpdateProperties();
|
||||||
TweetDeckBridge.RestoreSessionData(frame);
|
|
||||||
ScriptLoader.ExecuteFile(frame, "code.js", browser);
|
ScriptLoader.ExecuteFile(frame, "code.js", browser);
|
||||||
|
|
||||||
InjectBrowserCSS();
|
InjectBrowserCSS();
|
||||||
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
|
ReinjectCustomCSS(Config.CustomBrowserCSS);
|
||||||
UserConfig_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
Config_SoundNotificationInfoChanged(null, EventArgs.Empty);
|
||||||
|
|
||||||
TweetDeckBridge.ResetStaticProperties();
|
TweetDeckBridge.ResetStaticProperties();
|
||||||
|
|
||||||
@@ -162,13 +152,17 @@ namespace TweetDuck.Core{
|
|||||||
ScriptLoader.ExecuteScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
|
ScriptLoader.ExecuteScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.UserConfig.FirstRun){
|
if (Config.FirstRun){
|
||||||
ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
|
ScriptLoader.ExecuteFile(frame, "introduction.js", browser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptLoader.ExecuteFile(frame, "update.js", browser);
|
ScriptLoader.ExecuteFile(frame, "update.js", browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frame.Url == ErrorUrl){
|
||||||
|
resourceHandlerFactory.UnregisterHandler(ErrorUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
private void browser_LoadError(object sender, LoadErrorEventArgs e){
|
||||||
@@ -180,29 +174,34 @@ namespace TweetDuck.Core{
|
|||||||
string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html");
|
string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html");
|
||||||
|
|
||||||
if (errorPage != null){
|
if (errorPage != null){
|
||||||
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
|
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode))));
|
||||||
|
browser.Load(ErrorUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserConfig_MuteToggled(object sender, EventArgs e){
|
private void Config_MuteToggled(object sender, EventArgs e){
|
||||||
UpdateProperties();
|
UpdateProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
|
private void Config_SoundNotificationInfoChanged(object sender, EventArgs e){
|
||||||
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserConfig_SoundNotificationInfoChanged(object sender, EventArgs e){
|
|
||||||
const string soundUrl = "https://ton.twimg.com/tduck/updatesnd";
|
const string soundUrl = "https://ton.twimg.com/tduck/updatesnd";
|
||||||
bool hasCustomSound = Program.UserConfig.IsCustomSoundNotificationSet;
|
|
||||||
|
|
||||||
if (prevSoundNotificationPath != Program.UserConfig.NotificationSoundPath){
|
bool hasCustomSound = Config.IsCustomSoundNotificationSet;
|
||||||
browser.SetupResourceHandler(soundUrl, hasCustomSound ? SoundNotification.CreateFileHandler(Program.UserConfig.NotificationSoundPath) : null);
|
string newNotificationPath = Config.NotificationSoundPath;
|
||||||
prevSoundNotificationPath = Program.UserConfig.NotificationSoundPath;
|
|
||||||
|
if (prevSoundNotificationPath != newNotificationPath){
|
||||||
|
prevSoundNotificationPath = newNotificationPath;
|
||||||
|
|
||||||
|
if (hasCustomSound){
|
||||||
|
resourceHandlerFactory.RegisterHandler(soundUrl, SoundNotification.CreateFileHandler(newNotificationPath));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
resourceHandlerFactory.UnregisterHandler(soundUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Program.UserConfig.NotificationSoundVolume);
|
browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Config.NotificationSoundVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
// external handling
|
// external handling
|
||||||
|
@@ -6,8 +6,8 @@ using System.IO;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using CefSharp.WinForms;
|
using CefSharp.WinForms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Data;
|
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
static class BrowserUtils{
|
static class BrowserUtils{
|
||||||
@@ -16,13 +16,16 @@ namespace TweetDuck.Core.Utils{
|
|||||||
|
|
||||||
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
|
||||||
|
|
||||||
|
private static UserConfig Config => Program.Config.User;
|
||||||
|
private static SystemConfig SysConfig => Program.Config.System;
|
||||||
|
|
||||||
public static void SetupCefArgs(IDictionary<string, string> args){
|
public static void SetupCefArgs(IDictionary<string, string> args){
|
||||||
if (!Program.SystemConfig.HardwareAcceleration){
|
if (!SysConfig.HardwareAcceleration){
|
||||||
args["disable-gpu"] = "1";
|
args["disable-gpu"] = "1";
|
||||||
args["disable-gpu-vsync"] = "1";
|
args["disable-gpu-vsync"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Program.UserConfig.EnableSmoothScrolling){
|
if (Config.EnableSmoothScrolling){
|
||||||
args["disable-threaded-scrolling"] = "1";
|
args["disable-threaded-scrolling"] = "1";
|
||||||
|
|
||||||
if (args.TryGetValue("disable-features", out string disabledFeatures)){
|
if (args.TryGetValue("disable-features", out string disabledFeatures)){
|
||||||
@@ -36,7 +39,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
args["disable-smooth-scrolling"] = "1";
|
args["disable-smooth-scrolling"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Program.UserConfig.EnableTouchAdjustment){
|
if (!Config.EnableTouchAdjustment){
|
||||||
args["disable-touch-adjustment"] = "1";
|
args["disable-touch-adjustment"] = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,19 +59,19 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return (ChromiumWebBrowser)browserControl;
|
return (ChromiumWebBrowser)browserControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetupResourceHandler(this ChromiumWebBrowser browser, string url, IResourceHandler handler){
|
public static void SetupZoomEvents(this ChromiumWebBrowser browser){
|
||||||
DefaultResourceHandlerFactory factory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
|
void UpdateZoomLevel(object sender, EventArgs args){
|
||||||
|
SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
|
||||||
if (handler == null){
|
|
||||||
factory.UnregisterHandler(url);
|
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
factory.RegisterHandler(url, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetupResourceHandler(this ChromiumWebBrowser browser, ResourceLink resource){
|
Config.ZoomLevelChanged += UpdateZoomLevel;
|
||||||
browser.SetupResourceHandler(resource.Url, resource.Handler);
|
browser.Disposed += (sender, args) => Config.ZoomLevelChanged -= UpdateZoomLevel;
|
||||||
|
|
||||||
|
browser.FrameLoadStart += (sender, args) => {
|
||||||
|
if (args.Frame.IsMain && Config.ZoomLevel != 100){
|
||||||
|
SetZoomLevel(args.Browser, Config.ZoomLevel);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string TwitterTrackingUrl = "t.co";
|
private const string TwitterTrackingUrl = "t.co";
|
||||||
@@ -98,7 +101,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
FormGuide.Show(hash);
|
FormGuide.Show(hash);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
string browserPath = Program.UserConfig.BrowserPath;
|
string browserPath = Config.BrowserPath;
|
||||||
|
|
||||||
if (browserPath == null || !File.Exists(browserPath)){
|
if (browserPath == null || !File.Exists(browserPath)){
|
||||||
WindowsUtils.OpenAssociatedProgram(url);
|
WindowsUtils.OpenAssociatedProgram(url);
|
||||||
@@ -115,7 +118,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case UrlCheckResult.Tracking:
|
case UrlCheckResult.Tracking:
|
||||||
if (Program.UserConfig.IgnoreTrackingUrlWarning){
|
if (Config.IgnoreTrackingUrlWarning){
|
||||||
goto case UrlCheckResult.Fine;
|
goto case UrlCheckResult.Fine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +130,8 @@ namespace TweetDuck.Core.Utils{
|
|||||||
DialogResult result = form.ShowDialog();
|
DialogResult result = form.ShowDialog();
|
||||||
|
|
||||||
if (result == DialogResult.Ignore){
|
if (result == DialogResult.Ignore){
|
||||||
Program.UserConfig.IgnoreTrackingUrlWarning = true;
|
Config.IgnoreTrackingUrlWarning = true;
|
||||||
Program.UserConfig.Save();
|
Config.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == DialogResult.Ignore || result == DialogResult.Yes){
|
if (result == DialogResult.Ignore || result == DialogResult.Yes){
|
||||||
@@ -147,7 +150,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
public static void OpenExternalSearch(string query){
|
public static void OpenExternalSearch(string query){
|
||||||
if (string.IsNullOrWhiteSpace(query))return;
|
if (string.IsNullOrWhiteSpace(query))return;
|
||||||
|
|
||||||
string searchUrl = Program.UserConfig.SearchEngineUrl;
|
string searchUrl = Config.SearchEngineUrl;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(searchUrl)){
|
if (string.IsNullOrEmpty(searchUrl)){
|
||||||
if (FormMessage.Question("Search Options", "You have not configured a default search engine yet, would you like to do it now?", FormMessage.Yes, FormMessage.No)){
|
if (FormMessage.Question("Search Options", "You have not configured a default search engine yet, would you like to do it now?", FormMessage.Yes, FormMessage.No)){
|
||||||
@@ -160,7 +163,7 @@ namespace TweetDuck.Core.Utils{
|
|||||||
if (settings == null)return;
|
if (settings == null)return;
|
||||||
|
|
||||||
settings.FormClosed += (sender, args) => {
|
settings.FormClosed += (sender, args) => {
|
||||||
if (args.CloseReason == CloseReason.UserClosing && Program.UserConfig.SearchEngineUrl != searchUrl){
|
if (args.CloseReason == CloseReason.UserClosing && Config.SearchEngineUrl != searchUrl){
|
||||||
OpenExternalSearch(query);
|
OpenExternalSearch(query);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -188,9 +191,13 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
public static WebClient DownloadFileAsync(string url, string target, string cookie, Action onSuccess, Action<Exception> onFailure){
|
||||||
WebClient client = CreateWebClient();
|
WebClient client = CreateWebClient();
|
||||||
|
|
||||||
|
if (cookie != null){
|
||||||
|
client.Headers[HttpRequestHeader.Cookie] = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
client.DownloadFileCompleted += (sender, args) => {
|
client.DownloadFileCompleted += (sender, args) => {
|
||||||
if (args.Cancelled){
|
if (args.Cancelled){
|
||||||
try{
|
try{
|
||||||
|
@@ -30,11 +30,19 @@ namespace TweetDuck.Core.Utils{
|
|||||||
|
|
||||||
public sealed class Item : IComparable<Item>{
|
public sealed class Item : IComparable<Item>{
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
public CultureInfo Info { get; }
|
|
||||||
|
private string Name => info?.NativeName ?? Code;
|
||||||
|
|
||||||
|
private readonly CultureInfo info;
|
||||||
|
|
||||||
public Item(string code, string alt = null){
|
public Item(string code, string alt = null){
|
||||||
this.Code = code;
|
this.Code = code;
|
||||||
this.Info = CultureInfo.GetCultureInfo(alt ?? code);
|
|
||||||
|
try{
|
||||||
|
this.info = CultureInfo.GetCultureInfo(alt ?? code);
|
||||||
|
}catch(CultureNotFoundException){
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj){
|
public override bool Equals(object obj){
|
||||||
@@ -46,12 +54,16 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString(){
|
public override string ToString(){
|
||||||
string capitalizedName = Info.TextInfo.ToTitleCase(Info.NativeName);
|
if (info == null){
|
||||||
return Info.DisplayName == Info.NativeName ? capitalizedName : $"{capitalizedName}, {Info.DisplayName}";
|
return Code;
|
||||||
|
}
|
||||||
|
|
||||||
|
string capitalizedName = info.TextInfo.ToTitleCase(info.NativeName);
|
||||||
|
return info.DisplayName == info.NativeName ? capitalizedName : $"{capitalizedName}, {info.DisplayName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CompareTo(Item other){
|
public int CompareTo(Item other){
|
||||||
return string.Compare(Info.NativeName, other.Info.NativeName, false, CultureInfo.InvariantCulture);
|
return string.Compare(Name, other.Name, false, CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,18 +2,21 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Core.Management;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cookie = CefSharp.Cookie;
|
||||||
|
|
||||||
namespace TweetDuck.Core.Utils{
|
namespace TweetDuck.Core.Utils{
|
||||||
static class TwitterUtils{
|
static class TwitterUtils{
|
||||||
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
public const string TweetDeckURL = "https://tweetdeck.twitter.com";
|
||||||
|
|
||||||
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
|
||||||
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important}';h.appendChild(e);},1)";
|
public const string BackgroundColorOverride = "setTimeout(function f(){let h=document.head;if(!h){setTimeout(f,5);return;}let e=document.createElement('style');e.innerHTML='body,body::before{background:#1c6399!important;margin:0}';h.appendChild(e);},1)";
|
||||||
|
|
||||||
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
|
public static readonly ResourceLink LoadingSpinner = new ResourceLink("https://ton.twimg.com/tduck/spinner", ResourceHandler.FromByteArray(Properties.Resources.spinner, "image/apng"));
|
||||||
|
|
||||||
@@ -40,6 +43,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return frame.Url.Contains("//twitter.com/");
|
return frame.Url.Contains("//twitter.com/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsTwitterLogin2FactorWebsite(IFrame frame){
|
||||||
|
return frame.Url.Contains("//twitter.com/account/login_verification");
|
||||||
|
}
|
||||||
|
|
||||||
private static string ExtractMediaBaseLink(string url){
|
private static string ExtractMediaBaseLink(string url){
|
||||||
int slash = url.LastIndexOf('/');
|
int slash = url.LastIndexOf('/');
|
||||||
return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash);
|
return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash);
|
||||||
@@ -49,7 +56,10 @@ namespace TweetDuck.Core.Utils{
|
|||||||
if (quality == ImageQuality.Orig){
|
if (quality == ImageQuality.Orig){
|
||||||
string result = ExtractMediaBaseLink(url);
|
string result = ExtractMediaBaseLink(url);
|
||||||
|
|
||||||
if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
if (url.Contains("//ton.twitter.com/") && url.Contains("/ton/data/dm/")){
|
||||||
|
result += ":large";
|
||||||
|
}
|
||||||
|
else if (result != url || url.Contains("//pbs.twimg.com/media/")){
|
||||||
result += ":orig";
|
result += ":orig";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +74,32 @@ namespace TweetDuck.Core.Utils{
|
|||||||
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ViewImage(string url, ImageQuality quality){
|
||||||
|
void ViewImageInternal(string path){
|
||||||
|
string ext = Path.GetExtension(path);
|
||||||
|
|
||||||
|
if (ValidImageExtensions.Contains(ext)){
|
||||||
|
WindowsUtils.OpenAssociatedProgram(path);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string file = Path.Combine(BrowserCache.CacheFolder, GetImageFileName(url) ?? Path.GetRandomFileName());
|
||||||
|
|
||||||
|
if (WindowsUtils.FileExistsAndNotEmpty(file)){
|
||||||
|
ViewImageInternal(file);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
DownloadFileAuth(GetMediaLink(url, quality), file, () => {
|
||||||
|
ViewImageInternal(file);
|
||||||
|
}, ex => {
|
||||||
|
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void DownloadImage(string url, string username, ImageQuality quality){
|
public static void DownloadImage(string url, string username, ImageQuality quality){
|
||||||
DownloadImages(new string[]{ url }, username, quality);
|
DownloadImages(new string[]{ url }, username, quality);
|
||||||
}
|
}
|
||||||
@@ -92,14 +128,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (urls.Length == 1){
|
if (urls.Length == 1){
|
||||||
BrowserUtils.DownloadFileAsync(firstImageLink, dialog.FileName, null, OnFailure);
|
DownloadFileAuth(firstImageLink, dialog.FileName, null, OnFailure);
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
string pathBase = Path.ChangeExtension(dialog.FileName, null);
|
string pathBase = Path.ChangeExtension(dialog.FileName, null);
|
||||||
string pathExt = Path.GetExtension(dialog.FileName);
|
string pathExt = Path.GetExtension(dialog.FileName);
|
||||||
|
|
||||||
for(int index = 0; index < urls.Length; index++){
|
for(int index = 0; index < urls.Length; index++){
|
||||||
BrowserUtils.DownloadFileAsync(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
|
DownloadFileAuth(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,11 +154,33 @@ namespace TweetDuck.Core.Utils{
|
|||||||
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
|
||||||
}){
|
}){
|
||||||
if (dialog.ShowDialog() == DialogResult.OK){
|
if (dialog.ShowDialog() == DialogResult.OK){
|
||||||
BrowserUtils.DownloadFileAsync(url, dialog.FileName, null, ex => {
|
DownloadFileAuth(url, dialog.FileName, null, ex => {
|
||||||
FormMessage.Error("Video Download", "An error occurred while downloading the video: "+ex.Message, FormMessage.OK);
|
FormMessage.Error("Video Download", "An error occurred while downloading the video: "+ex.Message, FormMessage.OK);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DownloadFileAuth(string url, string target, Action onSuccess, Action<Exception> onFailure){
|
||||||
|
const string AuthCookieName = "auth_token";
|
||||||
|
|
||||||
|
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
|
||||||
|
|
||||||
|
using(ICookieManager cookies = Cef.GetGlobalCookieManager()){
|
||||||
|
cookies.VisitUrlCookiesAsync(url, true).ContinueWith(task => {
|
||||||
|
string cookieStr = null;
|
||||||
|
|
||||||
|
if (task.Status == TaskStatus.RanToCompletion){
|
||||||
|
Cookie found = task.Result?.Find(cookie => cookie.Name == AuthCookieName); // the list may be null
|
||||||
|
|
||||||
|
if (found != null){
|
||||||
|
cookieStr = $"{found.Name}={found.Value}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BrowserUtils.DownloadFileAsync(url, target, cookieStr, onSuccess, onFailure);
|
||||||
|
}, scheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -66,6 +66,14 @@ namespace TweetDuck.Core.Utils{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool FileExistsAndNotEmpty(string path){
|
||||||
|
try{
|
||||||
|
return new FileInfo(path).Length > 0;
|
||||||
|
}catch{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
|
||||||
try{
|
try{
|
||||||
using(Process.Start(new ProcessStartInfo{
|
using(Process.Start(new ProcessStartInfo{
|
||||||
|
@@ -89,7 +89,7 @@ namespace TweetDuck.Plugins.Controls{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void btnToggleState_Click(object sender, EventArgs e){
|
private void btnToggleState_Click(object sender, EventArgs e){
|
||||||
pluginManager.Config.ToggleEnabled(plugin);
|
pluginManager.Config.SetEnabled(plugin, !pluginManager.Config.IsEnabled(plugin));
|
||||||
UpdatePluginState();
|
UpdatePluginState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
Plugins/IPluginConfig.cs
Normal file
14
Plugins/IPluginConfig.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using TweetDuck.Plugins.Events;
|
||||||
|
|
||||||
|
namespace TweetDuck.Plugins{
|
||||||
|
interface IPluginConfig{
|
||||||
|
IEnumerable<string> DisabledPlugins { get; }
|
||||||
|
|
||||||
|
event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
||||||
|
|
||||||
|
void SetEnabled(Plugin plugin, bool enabled);
|
||||||
|
bool IsEnabled(Plugin plugin);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using TweetDuck.Plugins.Events;
|
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
|
||||||
sealed class PluginConfig{
|
|
||||||
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
|
||||||
|
|
||||||
public IEnumerable<string> DisabledPlugins => disabled;
|
|
||||||
public bool AnyDisabled => disabled.Count > 0;
|
|
||||||
|
|
||||||
private static readonly string[] DefaultDisabled = {
|
|
||||||
"official/clear-columns",
|
|
||||||
"official/reply-account"
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly HashSet<string> disabled = new HashSet<string>();
|
|
||||||
|
|
||||||
public void SetEnabled(Plugin plugin, bool enabled){
|
|
||||||
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
|
|
||||||
PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleEnabled(Plugin plugin){
|
|
||||||
SetEnabled(plugin, !IsEnabled(plugin));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsEnabled(Plugin plugin){
|
|
||||||
return !disabled.Contains(plugin.Identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save(string file){
|
|
||||||
try{
|
|
||||||
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
|
|
||||||
writer.WriteLine("#Disabled");
|
|
||||||
|
|
||||||
foreach(string identifier in disabled){
|
|
||||||
writer.WriteLine(identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load(string file){
|
|
||||||
try{
|
|
||||||
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
|
|
||||||
string line = reader.ReadLine();
|
|
||||||
|
|
||||||
if (line == "#Disabled"){
|
|
||||||
disabled.Clear();
|
|
||||||
|
|
||||||
while((line = reader.ReadLine()) != null){
|
|
||||||
disabled.Add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}catch(FileNotFoundException){
|
|
||||||
disabled.Clear();
|
|
||||||
disabled.UnionWith(DefaultDisabled);
|
|
||||||
Save(file);
|
|
||||||
}catch(DirectoryNotFoundException){
|
|
||||||
}catch(Exception e){
|
|
||||||
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -5,7 +5,7 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Core.Other.Interfaces;
|
using TweetDuck.Core.Utils;
|
||||||
using TweetDuck.Data;
|
using TweetDuck.Data;
|
||||||
using TweetDuck.Plugins.Enums;
|
using TweetDuck.Plugins.Enums;
|
||||||
using TweetDuck.Plugins.Events;
|
using TweetDuck.Plugins.Events;
|
||||||
@@ -21,35 +21,38 @@ namespace TweetDuck.Plugins{
|
|||||||
public IEnumerable<Plugin> Plugins => plugins;
|
public IEnumerable<Plugin> Plugins => plugins;
|
||||||
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
|
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
|
||||||
|
|
||||||
public PluginConfig Config { get; }
|
public IPluginConfig Config { get; }
|
||||||
|
|
||||||
public event EventHandler<PluginErrorEventArgs> Reloaded;
|
public event EventHandler<PluginErrorEventArgs> Reloaded;
|
||||||
public event EventHandler<PluginErrorEventArgs> Executed;
|
public event EventHandler<PluginErrorEventArgs> Executed;
|
||||||
|
|
||||||
private readonly string rootPath;
|
private readonly string rootPath;
|
||||||
private readonly string configPath;
|
|
||||||
private readonly PluginBridge bridge;
|
private readonly PluginBridge bridge;
|
||||||
|
|
||||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||||
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
||||||
private readonly Random rand = new Random();
|
private readonly Random rand = new Random();
|
||||||
|
|
||||||
private ITweetDeckBrowser mainBrowser;
|
private IWebBrowser mainBrowser;
|
||||||
|
|
||||||
|
public PluginManager(IPluginConfig config, string rootPath){
|
||||||
|
this.Config = config;
|
||||||
|
this.Config.PluginChangedState += Config_PluginChangedState;
|
||||||
|
|
||||||
public PluginManager(string rootPath, string configPath){
|
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
this.configPath = configPath;
|
|
||||||
|
|
||||||
this.Config = new PluginConfig();
|
|
||||||
this.bridge = new PluginBridge(this);
|
this.bridge = new PluginBridge(this);
|
||||||
|
|
||||||
Config.Load(configPath);
|
|
||||||
Config.PluginChangedState += Config_PluginChangedState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Register(ITweetDeckBrowser browser, PluginEnvironment environment, Control sync, bool asMainBrowser = false){
|
public void Register(IWebBrowser browser, PluginEnvironment environment, Control sync, bool asMainBrowser = false){
|
||||||
browser.OnFrameLoaded(frame => ExecutePlugins(frame, environment, sync));
|
browser.FrameLoadEnd += (sender, args) => {
|
||||||
browser.RegisterBridge("$TDP", bridge);
|
IFrame frame = args.Frame;
|
||||||
|
|
||||||
|
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
|
||||||
|
ExecutePlugins(frame, environment, sync);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
browser.RegisterAsyncJsObject("$TDP", bridge);
|
||||||
|
|
||||||
if (asMainBrowser){
|
if (asMainBrowser){
|
||||||
mainBrowser = browser;
|
mainBrowser = browser;
|
||||||
@@ -57,8 +60,7 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
|
||||||
mainBrowser?.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
mainBrowser?.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
|
||||||
Config.Save(configPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsPluginInstalled(string identifier){
|
public bool IsPluginInstalled(string identifier){
|
||||||
@@ -75,7 +77,7 @@ namespace TweetDuck.Plugins{
|
|||||||
|
|
||||||
public void ConfigurePlugin(Plugin plugin){
|
public void ConfigurePlugin(Plugin plugin){
|
||||||
if (bridge.WithConfigureFunction.Contains(plugin)){
|
if (bridge.WithConfigureFunction.Contains(plugin)){
|
||||||
mainBrowser?.ExecuteFunction("TDPF_configurePlugin", plugin);
|
mainBrowser?.ExecuteScriptAsync("TDPF_configurePlugin", plugin);
|
||||||
}
|
}
|
||||||
else if (plugin.HasConfig){
|
else if (plugin.HasConfig){
|
||||||
if (File.Exists(plugin.ConfigPath)){
|
if (File.Exists(plugin.ConfigPath)){
|
||||||
@@ -113,8 +115,6 @@ namespace TweetDuck.Plugins{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Reload(){
|
public void Reload(){
|
||||||
Config.Load(configPath);
|
|
||||||
|
|
||||||
plugins.Clear();
|
plugins.Clear();
|
||||||
tokens.Clear();
|
tokens.Clear();
|
||||||
|
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
using TweetDuck.Plugins.Enums;
|
using System.Linq;
|
||||||
|
using TweetDuck.Plugins.Enums;
|
||||||
|
|
||||||
namespace TweetDuck.Plugins{
|
namespace TweetDuck.Plugins{
|
||||||
static class PluginScriptGenerator{
|
static class PluginScriptGenerator{
|
||||||
public static string GenerateConfig(PluginConfig config){
|
public static string GenerateConfig(IPluginConfig config){
|
||||||
return config.AnyDisabled ? "window.TD_PLUGINS.disabled = [\""+string.Join("\",\"", config.DisabledPlugins)+"\"];" : string.Empty;
|
return "window.TD_PLUGINS.disabled = ["+string.Join(",", config.DisabledPlugins.Select(id => $"\"{id}\""))+"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){
|
||||||
|
18
Program.cs
18
Program.cs
@@ -1,4 +1,5 @@
|
|||||||
using CefSharp;
|
using CefSharp;
|
||||||
|
using CefSharp.WinForms;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@@ -8,6 +9,7 @@ using System.Threading;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using TweetDuck.Configuration;
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core;
|
using TweetDuck.Core;
|
||||||
|
using TweetDuck.Core.Handling;
|
||||||
using TweetDuck.Core.Handling.General;
|
using TweetDuck.Core.Handling.General;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
using TweetDuck.Core.Management;
|
using TweetDuck.Core.Management;
|
||||||
@@ -19,7 +21,7 @@ namespace TweetDuck{
|
|||||||
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.15.1";
|
public const string VersionTag = "1.17.4";
|
||||||
|
|
||||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
public static readonly bool IsPortable = File.Exists(Path.Combine(ProgramPath, "makeportable"));
|
||||||
@@ -50,10 +52,6 @@ namespace TweetDuck{
|
|||||||
public static Reporter Reporter { get; }
|
public static Reporter Reporter { get; }
|
||||||
public static ConfigManager Config { get; }
|
public static ConfigManager Config { get; }
|
||||||
|
|
||||||
// TODO
|
|
||||||
public static UserConfig UserConfig => Config.User;
|
|
||||||
public static SystemConfig SystemConfig => Config.System;
|
|
||||||
|
|
||||||
static Program(){
|
static Program(){
|
||||||
Culture = CultureInfo.CurrentCulture;
|
Culture = CultureInfo.CurrentCulture;
|
||||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||||
@@ -129,6 +127,14 @@ namespace TweetDuck{
|
|||||||
|
|
||||||
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
if (Arguments.HasFlag(Arguments.ArgUpdated)){
|
||||||
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
|
||||||
|
BrowserCache.TryClearNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze, null));
|
||||||
|
}catch(Exception e){
|
||||||
|
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserCache.RefreshTimer();
|
BrowserCache.RefreshTimer();
|
||||||
@@ -147,7 +153,7 @@ namespace TweetDuck{
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
CommandLineArgs.ReadCefArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||||
|
|
||||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||||
|
22
Properties/Resources.Designer.cs
generated
22
Properties/Resources.Designer.cs
generated
@@ -19,7 +19,7 @@ namespace TweetDuck.Properties {
|
|||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources {
|
internal class Resources {
|
||||||
@@ -120,6 +120,26 @@ namespace TweetDuck.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Icon overlay_muted {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("overlay_muted", resourceCulture);
|
||||||
|
return ((System.Drawing.Icon)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Icon overlay_notification {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("overlay_notification", resourceCulture);
|
||||||
|
return ((System.Drawing.Icon)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized resource of type System.Byte[].
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -136,6 +136,12 @@
|
|||||||
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\Images\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
<value>..\Resources\Images\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="overlay_muted" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Images\overlay-muted.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
|
<data name="overlay_notification" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\Images\overlay-notification.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
|
</data>
|
||||||
<data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\Images\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>..\Resources\Images\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</data>
|
</data>
|
||||||
|
28
README.md
28
README.md
@@ -6,25 +6,25 @@
|
|||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
The program was built using Visual Studio 2017. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
The program can be built using Visual Studio 2017 or newer. Before opening the solution, please make sure you have the following workloads and components installed (optional components that are not listed can be deselected to save space):
|
||||||
* **.NET desktop development**
|
* **.NET desktop development**
|
||||||
* .NET Framework 4 – 4.6 development tools
|
* .NET Framework 4 – 4.6 development tools
|
||||||
* F# desktop language support
|
* F# desktop language support
|
||||||
* **Desktop development with C++**
|
* **Desktop development with C++**
|
||||||
* VC++ 2017 latest v141 tools
|
* *(VS 2017)* VC++ 2017 version 15.9 v14.16 latest v141 tools
|
||||||
|
* *(VS 2019)* MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20)
|
||||||
|
|
||||||
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
|
After opening the solution, right-click the solution and select **Restore NuGet Packages**, or manually run this command in the **Package Manager Console**:
|
||||||
```
|
```
|
||||||
PM> Install-Package CefSharp.WinForms -Version 67.0.0-CI2662 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
|
PM> Install-Package CefSharp.WinForms -Version 67.0.0
|
||||||
|
PM> Install-Package WindowsAPICodePack-Shell -Version 1.1.1
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
|
|
||||||
|
|
||||||
### Debug
|
### Debug
|
||||||
|
|
||||||
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
|
The `Debug` configuration uses a separate data folder by default (`%LOCALAPPDATA%\TweetDuckDebug`) to avoid affecting an existing installation of TweetDuck. You can modify this by opening **TweetDuck Properties** in Visual Studio, clicking the **Debug** tab, and changing the **Command line arguments** field.
|
||||||
|
|
||||||
While debugging, opening the main menu and clicking **Reload browser** automatically rebuilds all resources in the `Resources/Scripts` and `Resources/Plugins` folders. This allows editing HTML/CSS/JS files and applying the changes without restarting the program, but it will cause a short delay between browser reloads.
|
While debugging, opening the main menu and clicking **Reload browser** automatically rebuilds all resources in `Resources/Scripts` and `Resources/Plugins`. This allows editing HTML/CSS/JS files without restarting the program, but it will cause a short delay between browser reloads. An F# compiler must be present when building the project to enable this feature: `C:\Program Files (x86)\Microsoft SDKs\F#\10.1\Framework\v4.0\fsc.exe`
|
||||||
|
|
||||||
### Release
|
### Release
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ Open **Batch Build**, tick all `Release` configurations with `x86` platform, and
|
|||||||
|
|
||||||
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
|
||||||
|
|
||||||
The `Release` configuration omits debug symbols and other unnecessary files. You can modify this behavior by opening `TweetDuck.csproj`, and editing the `<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">` section.
|
The `Release` configuration omits debug symbols and other unnecessary files, and it will automatically generate the [update installer](#installers) if the environment is setup correctly. You can modify this behavior by opening `TweetDuck.csproj`, and editing the `<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">` section.
|
||||||
|
|
||||||
If you decide to publicly release a custom version, please make it clear that it is not an official release of TweetDuck. There are many references to the official website and this repository, especially in the update system, so search for `chylex.com` and `github.com` in all files and replace them appropriately.
|
If you decide to publicly release a custom version, please make it clear that it is not an official release of TweetDuck. There are many references to the official website and this repository, especially in the update system, so search for `chylex.com` and `github.com` in all files and replace them appropriately.
|
||||||
|
|
||||||
@@ -40,12 +40,12 @@ If you decide to publicly release a custom version, please make it clear that it
|
|||||||
|
|
||||||
#### Error: The command (...) exited with code 1
|
#### Error: The command (...) exited with code 1
|
||||||
- This indicates a failed post-build event, open the **Output** tab for logs
|
- This indicates a failed post-build event, open the **Output** tab for logs
|
||||||
- Determine if there was an IO error from the `rmdir` commands, or whether the error was in the **PostBuild.ps1** script (`Encountered an error while running PostBuild.ps1 on line <xyz>`)
|
- Determine if there was an IO error from the `rmdir` commands, the custom MSBuild targets near the end of the [.csproj file](https://github.com/chylex/TweetDuck/blob/master/TweetDuck.csproj), or in the **PostBuild.fsx** script (`Encountered an error while running PostBuild`)
|
||||||
- Some files are checked for invalid characters:
|
- Some files are checked for invalid characters:
|
||||||
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
|
||||||
|
|
||||||
#### Error: The "EmbedAllSources" parameter is not supported by the "Csc" task
|
#### Error: The "EmbedAllSources" parameter is not supported by the "Csc" task
|
||||||
1. Open `C:\Program Files (x86)\Visual Studio\2017\<edition>\MSBuild\15.0\Bin\Microsoft.CSharp.CurrentVersion.targets` in a text editor
|
1. Open `C:\Program Files (x86)\Visual Studio\2017\<edition>\MSBuild\15.0\Bin\Roslyn\Microsoft.CSharp.Core.targets` in a text editor
|
||||||
2. Remove line that says `EmbedAllSources="$(EmbedAllSources)"`
|
2. Remove line that says `EmbedAllSources="$(EmbedAllSources)"`
|
||||||
3. Hope the next Visual Studio update fixes it...
|
3. Hope the next Visual Studio update fixes it...
|
||||||
|
|
||||||
@@ -55,22 +55,24 @@ TweetDuck uses **Inno Setup** for installers and updates. First, download and in
|
|||||||
|
|
||||||
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
|
||||||
|
|
||||||
Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that this will only package the files, you still need to run the [release build](#release) in Visual Studio!
|
Now you can generate installers by running `bld/GEN INSTALLERS.bat`. Note that this will only package the files, you still need to run the [release build](#release) in Visual Studio!
|
||||||
|
|
||||||
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
After the window closes, three installers will be generated inside the `bld/Output` folder:
|
||||||
* **TweetDuck.exe**
|
* **TweetDuck.exe**
|
||||||
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
|
||||||
* **TweetDuck.Update.exe**
|
* **TweetDuck.Update.exe**
|
||||||
* This is a lightweight update installer that only contains the most important files that usually change across releases
|
* This is a lightweight update installer that only contains the most important files that usually change across releases
|
||||||
* It will automatically download and apply the full installer if the user's current version of CEF does not match (the download link is in `gen_upd.iss` and points to this repository by default)
|
* It will automatically download and apply the full installer if the user's current version of CEF does not match
|
||||||
* **TweetDuck.Portable.exe**
|
* **TweetDuck.Portable.exe**
|
||||||
* This is a portable installer that does not need administrator privileges
|
* This is a portable installer that does not need administrator privileges
|
||||||
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
|
* It automatically creates a `makeportable` file in the program folder, which forces TweetDuck to run in portable mode
|
||||||
|
|
||||||
|
The installers are built for GitHub Releases, where the main and portable installers can download and install Visual C++ if it's missing, and the update installer will download and apply the full installer when needed. If you plan to distribute your own installers via GitHub, you can change the variables in the installer files (`.iss`) and in the update system to point to your repository, and use the power of the existing update system.
|
||||||
|
|
||||||
#### Notes
|
#### Notes
|
||||||
|
|
||||||
> When opening **Batch Build**, you will also see `x64` and `AnyCPU` configurations. These are visible due to what I consider a Visual Studio bug, and will not work without significant changes to the project. Manually running the `Resources/PostCefUpdate.ps1` PowerShell script modifies the downloaded CefSharp packages, and removes the invalid configurations.
|
> When opening **Batch Build**, you will also see `x64` and `AnyCPU` configurations. These are visible due to what I consider a Visual Studio bug, and will not work without significant changes to the project. Manually running the `Resources/PostCefUpdate.ps1` PowerShell script modifies the downloaded CefSharp packages, and removes the invalid configurations.
|
||||||
|
|
||||||
> There is a small chance running `RUN BUILD.bat` immediately shows a resource error. If that happens, close the console window (which terminates all Inno Setup processes and leaves corrupted installers in the output folder), and run it again.
|
> There is a small chance running `GEN INSTALLERS.bat` immediately shows a resource error. If that happens, close the console window (which terminates all Inno Setup processes and leaves corrupted installers in the output folder), and run it again.
|
||||||
|
|
||||||
> Running `RUN BUILD.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`.
|
> Running `GEN INSTALLERS.bat` uses about 400 MB of RAM due to high compression. You can lower this to about 140 MB by opening `gen_full.iss` and `gen_port.iss`, and changing `LZMADictionarySize=15360` to `LZMADictionarySize=4096`.
|
||||||
|
@@ -4,6 +4,7 @@ using System.Drawing;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
using TweetDuck.Configuration;
|
||||||
using TweetDuck.Core.Other;
|
using TweetDuck.Core.Other;
|
||||||
|
|
||||||
namespace TweetDuck{
|
namespace TweetDuck{
|
||||||
@@ -22,7 +23,11 @@ namespace TweetDuck{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Log(string data){
|
public bool LogVerbose(string data){
|
||||||
|
return Arguments.HasFlag(Arguments.ArgLogging) && LogImportant(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LogImportant(string data){
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Debug.WriteLine(data);
|
Debug.WriteLine(data);
|
||||||
#endif
|
#endif
|
||||||
@@ -45,7 +50,7 @@ namespace TweetDuck{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
public void HandleException(string caption, string message, bool canIgnore, Exception e){
|
||||||
bool loggedSuccessfully = Log(e.ToString());
|
bool loggedSuccessfully = LogImportant(e.ToString());
|
||||||
|
|
||||||
string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message;
|
string exceptionText = e is ExpandedLogException ? e.Message+"\n\nDetails with potentially sensitive information are in the Error Log." : e.Message;
|
||||||
FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
FormMessage form = new FormMessage(caption, message+"\nError: "+exceptionText, canIgnore ? MessageBoxIcon.Warning : MessageBoxIcon.Error);
|
||||||
|
BIN
Resources/Design/overlay_icons.afdesign
Normal file
BIN
Resources/Design/overlay_icons.afdesign
Normal file
Binary file not shown.
BIN
Resources/Images/overlay-muted.ico
Normal file
BIN
Resources/Images/overlay-muted.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
BIN
Resources/Images/overlay-notification.ico
Normal file
BIN
Resources/Images/overlay-notification.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
3
Resources/Plugins/.debug/notification.js
Normal file
3
Resources/Plugins/.debug/notification.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
run(){
|
||||||
|
console.info("executed debug plugin in notification");
|
||||||
|
}
|
@@ -87,14 +87,14 @@ enabled(){
|
|||||||
// update UI
|
// update UI
|
||||||
|
|
||||||
this.btnClearAllHTML = `
|
this.btnClearAllHTML = `
|
||||||
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--10" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
|
<a class="clear-columns-btn-all-parent js-header-action link-clean cf app-nav-link padding-h--16 padding-v--2" data-title="Clear columns (hold Shift to restore)" data-action="td-clearcolumns-doall">
|
||||||
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
|
<div class="obj-left margin-l--2"><i class="icon icon-medium icon-clear-timeline"></i></div>
|
||||||
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--16 app-nav-link-text">Clear columns</div>
|
<div class="clear-columns-btn-all nbfc padding-ts hide-condensed txt-size--14 app-nav-link-text">Clear columns</div>
|
||||||
</a>`;
|
</a>`;
|
||||||
|
|
||||||
this.btnClearOneHTML = `
|
this.btnClearOneHTML = `
|
||||||
<a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle">
|
<a class="js-action-header-button column-header-link" href="#" data-action="td-clearcolumns-dosingle">
|
||||||
<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)" data-action="td-clearcolumns-dosingle"></i>
|
<i class="icon icon-clear-timeline js-show-tip" data-placement="bottom" data-original-title="Clear column (hold Shift to restore)" data-action="td-clearcolumns-dosingle"></i>
|
||||||
</a>`;
|
</a>`;
|
||||||
|
|
||||||
this.prevNavMenuMustache = TD.mustaches["menus/column_nav_menu.mustache"];
|
this.prevNavMenuMustache = TD.mustaches["menus/column_nav_menu.mustache"];
|
||||||
@@ -110,16 +110,28 @@ enabled(){
|
|||||||
|
|
||||||
// styles
|
// styles
|
||||||
|
|
||||||
|
if (!document.getElementById("td-clearcolumns-workaround")){
|
||||||
|
// TD started caching mustaches so disabling the plugin doesn't update the column headers properly...
|
||||||
|
let workaround = document.createElement("style");
|
||||||
|
workaround.id = "td-clearcolumns-workaround";
|
||||||
|
workaround.innerText = "#tduck a[data-action='td-clearcolumns-dosingle'] { display: none }";
|
||||||
|
document.head.appendChild(workaround);
|
||||||
|
}
|
||||||
|
|
||||||
this.css = window.TDPF_createCustomStyle(this);
|
this.css = window.TDPF_createCustomStyle(this);
|
||||||
|
|
||||||
this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
this.css.insert(".js-app-add-column.is-hidden + .clear-columns-btn-all-parent { display: none; }");
|
||||||
this.css.insert(".column-header-links { min-width: 51px !important; }");
|
|
||||||
this.css.insert("[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
|
|
||||||
this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
this.css.insert(".column-navigator-overflow .clear-columns-btn-all-parent { display: none !important; }");
|
||||||
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
this.css.insert(".column-navigator-overflow { bottom: 224px !important; }");
|
||||||
this.css.insert("[data-action='td-clearcolumns-dosingle'] { padding: 3px 0 !important; }");
|
this.css.insert(".app-navigator .clear-columns-btn-all-parent { font-weight: 700; }");
|
||||||
this.css.insert("[data-action='clear'].btn-options-tray { display: none !important; }");
|
|
||||||
this.css.insert("[data-td-icon='icon-schedule'] .td-clear-column-shortcut { display: none; }");
|
this.css.insert(".column-header-links { min-width: 51px !important; }");
|
||||||
this.css.insert("[data-td-icon='icon-custom-timeline'] .td-clear-column-shortcut { display: none; }");
|
this.css.insert(".column[data-td-icon='icon-message'] .column-header-links { min-width: 110px !important; }");
|
||||||
|
this.css.insert(".btn-options-tray[data-action='clear'] { display: none !important; }");
|
||||||
|
|
||||||
|
this.css.insert("#tduck a[data-action='td-clearcolumns-dosingle'] { display: inline-block; }");
|
||||||
|
this.css.insert("#tduck .column[data-td-icon='icon-schedule'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
|
||||||
|
this.css.insert("#tduck .column[data-td-icon='icon-custom-timeline'] a[data-action='td-clearcolumns-dosingle'] { display: none; }");
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
|
@@ -2,7 +2,6 @@ enabled(){
|
|||||||
// elements & data
|
// elements & data
|
||||||
this.css = null;
|
this.css = null;
|
||||||
this.icons = null;
|
this.icons = null;
|
||||||
this.htmlModal = null;
|
|
||||||
this.config = null;
|
this.config = null;
|
||||||
|
|
||||||
this.defaultConfig = {
|
this.defaultConfig = {
|
||||||
@@ -15,6 +14,7 @@ enabled(){
|
|||||||
themeColorTweaks: true,
|
themeColorTweaks: true,
|
||||||
revertIcons: true,
|
revertIcons: true,
|
||||||
showCharacterCount: true,
|
showCharacterCount: true,
|
||||||
|
forceArialFont: true,
|
||||||
increaseQuoteTextSize: false,
|
increaseQuoteTextSize: false,
|
||||||
smallComposeTextSize: false,
|
smallComposeTextSize: false,
|
||||||
optimizeAnimations: true,
|
optimizeAnimations: true,
|
||||||
@@ -41,13 +41,6 @@ enabled(){
|
|||||||
|
|
||||||
const me = this;
|
const me = this;
|
||||||
|
|
||||||
// modal dialog loading
|
|
||||||
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
|
|
||||||
this.htmlModal = contents;
|
|
||||||
}).catch(err => {
|
|
||||||
$TD.alert("error", "Problem loading data for the design edit plugin: "+err.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
// configuration
|
// configuration
|
||||||
const configFile = "config.json";
|
const configFile = "config.json";
|
||||||
|
|
||||||
@@ -112,7 +105,7 @@ enabled(){
|
|||||||
|
|
||||||
// settings click event
|
// settings click event
|
||||||
this.onSettingsMenuClickedEvent = () => {
|
this.onSettingsMenuClickedEvent = () => {
|
||||||
return if this.htmlModal === null || this.config === null;
|
return if this.config === null;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
let menu = $(".js-dropdown-content").children("ul").first();
|
let menu = $(".js-dropdown-content").children("ul").first();
|
||||||
@@ -181,20 +174,26 @@ enabled(){
|
|||||||
// SELECTS
|
// SELECTS
|
||||||
else if (tag === "SELECT"){
|
else if (tag === "SELECT"){
|
||||||
let optionCustom = item.find("option[value^='custom']");
|
let optionCustom = item.find("option[value^='custom']");
|
||||||
|
let optionCustomNew = item.find("option[value^='change-custom']");
|
||||||
|
|
||||||
let resetMyValue = () => {
|
let resetMyValue = () => {
|
||||||
if (!item.val(me.config[key]).val() && optionCustom.length === 1){
|
if (!item.val(me.config[key]).val() && optionCustom.length === 1){
|
||||||
item.val(optionCustom.attr("value"));
|
item.val(optionCustom.attr("value"));
|
||||||
optionCustom.text(getTextForCustom(key));
|
optionCustom.text(getTextForCustom(key));
|
||||||
|
optionCustomNew.show();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
optionCustom.text("Custom");
|
||||||
|
optionCustomNew.hide();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
resetMyValue();
|
resetMyValue();
|
||||||
|
|
||||||
item.change(function(){ // TODO change doesn't fire when Custom is already selected
|
item.change(function(){
|
||||||
let val = item.val();
|
let val = item.val();
|
||||||
|
|
||||||
if (val === "custom-px"){
|
if (val.endsWith("custom-px")){
|
||||||
val = (prompt("Enter custom value (px):") || "").trim();
|
val = (prompt("Enter custom value (px):") || "").trim();
|
||||||
|
|
||||||
if (val){
|
if (val){
|
||||||
@@ -204,21 +203,17 @@ enabled(){
|
|||||||
|
|
||||||
if (/^[0-9]+$/.test(val)){
|
if (/^[0-9]+$/.test(val)){
|
||||||
updateKey(key, val+"px");
|
updateKey(key, val+"px");
|
||||||
optionCustom.text(getTextForCustom(key));
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
alert("Invalid value, only px values are supported.");
|
alert("Invalid value, only px values are supported.");
|
||||||
resetMyValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
resetMyValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
updateKey(key, item.val());
|
updateKey(key, item.val());
|
||||||
optionCustom.text("Custom");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetMyValue();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// CUSTOM ELEMENTS
|
// CUSTOM ELEMENTS
|
||||||
@@ -260,7 +255,7 @@ enabled(){
|
|||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
if (theme != TD.settings.getTheme()){
|
if (theme != TD.settings.getTheme()){
|
||||||
$(document).trigger("uiToggleTheme");
|
TD.settings.setTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
me.saveConfig();
|
me.saveConfig();
|
||||||
@@ -268,13 +263,17 @@ enabled(){
|
|||||||
}, 1);
|
}, 1);
|
||||||
});
|
});
|
||||||
}).methods({
|
}).methods({
|
||||||
_render: () => $(this.htmlModal),
|
_render: function(){
|
||||||
|
return $(me.htmlModal);
|
||||||
|
},
|
||||||
destroy: function(){
|
destroy: function(){
|
||||||
if (this.reloadPage){
|
if (this.reloadPage){
|
||||||
window.TDPF_requestReload();
|
window.TDPF_requestReload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete me.htmlModal;
|
||||||
|
|
||||||
$("#td-design-plugin-modal").hide();
|
$("#td-design-plugin-modal").hide();
|
||||||
this.supr();
|
this.supr();
|
||||||
}
|
}
|
||||||
@@ -432,6 +431,11 @@ enabled(){
|
|||||||
this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }");
|
this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.forceArialFont){
|
||||||
|
this.css.insert("#tduck { font-family: Arial, sans-serif; font-weight: 400 }");
|
||||||
|
this.css.insert("#tduck input, #tduck label, #tduck select, #tduck textarea { font-family: Arial }")
|
||||||
|
}
|
||||||
|
|
||||||
if (this.config.increaseQuoteTextSize){
|
if (this.config.increaseQuoteTextSize){
|
||||||
this.css.insert(".quoted-tweet { font-size: 1em !important }");
|
this.css.insert(".quoted-tweet { font-size: 1em !important }");
|
||||||
}
|
}
|
||||||
@@ -538,11 +542,13 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
|
|||||||
|
|
||||||
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
|
||||||
.app-search-fake .icon { margin-top: -3px !important }
|
.app-search-fake .icon { margin-top: -3px !important }
|
||||||
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
|
|
||||||
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
|
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
|
||||||
|
#tduck .js-docked-compose .js-drawer-close { margin: 20px 0 0 !important }
|
||||||
|
#tduck .compose-media-bar-remove .icon-close, #tduck .compose-media-grid-remove .icon-close { padding: 3px 2px 1px !important }
|
||||||
|
|
||||||
.js-column-header .column-type-icon { margin-top: 0 !important }
|
.js-column-header .column-type-icon { margin-top: 0 !important }
|
||||||
.inline-reply .pull-left .Button--link { margin-top: 3px !important }
|
.inline-reply .pull-left .Button--link { margin-top: 3px !important }
|
||||||
|
.js-inline-compose-pop .icon-popout { font-size: 23px !important }
|
||||||
|
|
||||||
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
.tweet-action-item .icon-favorite-toggle { font-size: 16px !important; }
|
||||||
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
.tweet-action-item .heartsprite { top: -260% !important; left: -260% !important; transform: scale(0.4, 0.39) translateY(0.5px) !important; }
|
||||||
@@ -597,6 +603,10 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
|
|||||||
html[data-td-font] { font-size: ${this.config.fontSize} !important }
|
html[data-td-font] { font-size: ${this.config.fontSize} !important }
|
||||||
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
.avatar { border-radius: ${this.config.avatarRadius}% !important }
|
||||||
|
|
||||||
|
${this.config.forceArialFont ? `
|
||||||
|
#tduck { font-family: Arial, sans-serif; font-weight: 400 }
|
||||||
|
` : ``}
|
||||||
|
|
||||||
${this.config.increaseQuoteTextSize ? `
|
${this.config.increaseQuoteTextSize ? `
|
||||||
.quoted-tweet { font-size: 1em !important }
|
.quoted-tweet { font-size: 1em !important }
|
||||||
` : ``}
|
` : ``}
|
||||||
@@ -670,7 +680,12 @@ ready(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
configure(){
|
configure(){
|
||||||
new this.customDesignModal();
|
$TDP.readFileRoot(this.$token, "modal.html").then(contents => {
|
||||||
|
this.htmlModal = contents;
|
||||||
|
new this.customDesignModal();
|
||||||
|
}).catch(err => {
|
||||||
|
$TD.alert("error", "Error loading the configuration dialog: "+err.message);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disabled(){
|
disabled(){
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
<option value="350px">Wide (350px)</option>
|
<option value="350px">Wide (350px)</option>
|
||||||
<option value="400px">Extreme (400px)</option>
|
<option value="400px">Extreme (400px)</option>
|
||||||
<option value="custom-px">Custom</option>
|
<option value="custom-px">Custom</option>
|
||||||
|
<option value="change-custom-px">Change custom value...</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<option disabled></option>
|
<option disabled></option>
|
||||||
<optgroup label="Dynamic width">
|
<optgroup label="Dynamic width">
|
||||||
@@ -66,7 +67,12 @@
|
|||||||
<option value="15px">Large (15px)</option>
|
<option value="15px">Large (15px)</option>
|
||||||
<option value="16px">Largest (16px)</option>
|
<option value="16px">Largest (16px)</option>
|
||||||
<option value="custom-px">Custom</option>
|
<option value="custom-px">Custom</option>
|
||||||
|
<option value="change-custom-px">Change custom value...</option>
|
||||||
</select>
|
</select>
|
||||||
|
<label class="checkbox">
|
||||||
|
<input data-td-key="forceArialFont" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
|
Use Arial as default font
|
||||||
|
</label>
|
||||||
<label class="checkbox">
|
<label class="checkbox">
|
||||||
<input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
<input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
|
||||||
Increase quoted tweet font size
|
Increase quoted tweet font size
|
||||||
@@ -167,12 +173,23 @@
|
|||||||
#edit-design-panel {
|
#edit-design-panel {
|
||||||
width: 693px;
|
width: 693px;
|
||||||
height: 380px;
|
height: 380px;
|
||||||
|
background-color: #FFF;
|
||||||
|
box-shadow: 0 0 10px rgba(17, 17, 17, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#edit-design-panel .mdl-header {
|
||||||
|
color: #8899A6;
|
||||||
}
|
}
|
||||||
|
|
||||||
#edit-design-panel .mdl-inner {
|
#edit-design-panel .mdl-inner {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#edit-design-panel .mdl-content {
|
||||||
|
border: 1px solid #CCD6DD;
|
||||||
|
background: #EAEAEA;
|
||||||
|
}
|
||||||
|
|
||||||
#edit-design-panel-inner-cols {
|
#edit-design-panel-inner-cols {
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
}
|
}
|
||||||
|
@@ -117,7 +117,7 @@ html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
|
|||||||
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
|
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
|
||||||
html.dark .account-stats a{color:#66757f}
|
html.dark .account-stats a{color:#66757f}
|
||||||
html.dark .account-stats a:hover{color:#2b7bb9}
|
html.dark .account-stats a:hover{color:#2b7bb9}
|
||||||
html.dark .column{background-color:#222426}
|
html.dark .column-panel{background-color:#222426}
|
||||||
html.dark .column.is-focused{box-shadow:0 0 0 6px #7aa2c0}
|
html.dark .column.is-focused{box-shadow:0 0 0 6px #7aa2c0}
|
||||||
html.dark .column-background-fill{background-color:#F5F8FA}
|
html.dark .column-background-fill{background-color:#F5F8FA}
|
||||||
html.dark .more-tweets-glow{background-color:#55acee;background:radial-gradient(ellipse farthest-corner at 50% 100%,#55acee 0%,#55acee 25%,rgba(255,255,255,0) 75%)}
|
html.dark .more-tweets-glow{background-color:#55acee;background:radial-gradient(ellipse farthest-corner at 50% 100%,#55acee 0%,#55acee 25%,rgba(255,255,255,0) 75%)}
|
||||||
@@ -135,7 +135,6 @@ html.dark .column-header{background-color:#292F33}
|
|||||||
html.dark .is-inverted-dark .column-header{border-bottom:1px solid #ddd}
|
html.dark .is-inverted-dark .column-header{border-bottom:1px solid #ddd}
|
||||||
html.dark .is-inverted-dark .column-title-edit-box{color:#111;background-color:#fff;border-color:#e1e8ed}
|
html.dark .is-inverted-dark .column-title-edit-box{color:#111;background-color:#fff;border-color:#e1e8ed}
|
||||||
html.dark .column-header{border-bottom:1px solid #222426}
|
html.dark .column-header{border-bottom:1px solid #222426}
|
||||||
html.dark .column-header-temp{border-bottom:1px solid #ddd}
|
|
||||||
html.dark .column-title-edit-box{color:#e1e8ed;background-color:#14171A;border-color:#111}
|
html.dark .column-title-edit-box{color:#e1e8ed;background-color:#14171A;border-color:#111}
|
||||||
html.dark .column-number{color:#66757f}
|
html.dark .column-number{color:#66757f}
|
||||||
html.dark .is-new .column-type-icon{color:#55acee}
|
html.dark .is-new .column-type-icon{color:#55acee}
|
||||||
@@ -428,8 +427,7 @@ html.dark .list-account .username{color:#8899a6}
|
|||||||
html.dark .list-listmember .username{color:#8899a6}
|
html.dark .list-listmember .username{color:#8899a6}
|
||||||
html.dark .list-listmember .bio{color:#657786}
|
html.dark .list-listmember .bio{color:#657786}
|
||||||
html.dark .divider-bar{background-color:#ddd}
|
html.dark .divider-bar{background-color:#ddd}
|
||||||
html.dark select{background-color:#fff}
|
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed;background-color:#fff}
|
||||||
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed}
|
|
||||||
html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed}
|
html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed}
|
||||||
html.dark select:disabled{background-color:#f5f8fa}
|
html.dark select:disabled{background-color:#f5f8fa}
|
||||||
html.dark input:focus,html.dark select:focus,html.dark textarea:focus,html.dark .focus{border-color:rgba(80,165,230,0.8);box-shadow:inset 0 1px 3px rgba(17,17,17,0.1),0 0 8px rgba(80,165,230,0.6)}
|
html.dark input:focus,html.dark select:focus,html.dark textarea:focus,html.dark .focus{border-color:rgba(80,165,230,0.8);box-shadow:inset 0 1px 3px rgba(17,17,17,0.1),0 0 8px rgba(80,165,230,0.6)}
|
||||||
@@ -802,5 +800,12 @@ html.dark .NotificationList .Notification-body{color:#14171A}
|
|||||||
html.dark .DrawerModal{color:#14171A}
|
html.dark .DrawerModal{color:#14171A}
|
||||||
/* fixes */
|
/* fixes */
|
||||||
html.dark .app-search-fake{border-color:transparent}
|
html.dark .app-search-fake{border-color:transparent}
|
||||||
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}
|
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(85%)brightness(117%)}
|
||||||
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
|
html.dark .tweet>.color-twitter-blue{color:#8bd!important}
|
||||||
|
html.dark .hw-card-container>div{border-color:#292F33;background:transparent}
|
||||||
|
html.dark .hw-card-container>div>div{border-color:#292F33}
|
||||||
|
html.dark .modal-content,html.dark .lst-group,html.dark #actions-modal{color:#292F33}
|
||||||
|
html.dark #actions-modal .account-link{color:#38444d}
|
||||||
|
html.dark .mdl-accent{background-color:transparent}
|
||||||
|
html.dark .lst-launcher a span{color:#657786!important}
|
||||||
|
html.dark .social-proof-names a{color:#3b94d9}
|
||||||
|
@@ -56,11 +56,13 @@ enabled(){
|
|||||||
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
this.prevComposeMustache = TD.mustaches["compose/docked_compose.mustache"];
|
||||||
window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
|
window.TDPF_injectMustache("compose/docked_compose.mustache", "append", '<div class="cf margin-t--12 margin-b--30">', buttonHTML);
|
||||||
|
|
||||||
let maybeDockedComposePanel = $(".js-docked-compose");
|
this.getDrawerInput = () => {
|
||||||
|
return $(".js-compose-text", me.composeDrawer);
|
||||||
|
};
|
||||||
|
|
||||||
if (maybeDockedComposePanel.length){
|
this.getDrawerScroller = () => {
|
||||||
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
return $(".js-compose-scroller > .scroll-v", me.composeDrawer);
|
||||||
}
|
};
|
||||||
|
|
||||||
// keyboard generation
|
// keyboard generation
|
||||||
|
|
||||||
@@ -79,12 +81,12 @@ enabled(){
|
|||||||
|
|
||||||
this.currentKeywords = [];
|
this.currentKeywords = [];
|
||||||
|
|
||||||
this.composePanelScroller.trigger("scroll");
|
this.getDrawerScroller().trigger("scroll");
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
$(".emoji-keyboard-popup-btn").removeClass("is-selected");
|
||||||
|
|
||||||
if (refocus){
|
if (refocus){
|
||||||
this.composeInput.focus();
|
this.getDrawerInput().focus();
|
||||||
|
|
||||||
if (lastEmojiKeyword && lastEmojiPosition === 0){
|
if (lastEmojiKeyword && lastEmojiPosition === 0){
|
||||||
document.execCommand("insertText", false, lastEmojiKeyword);
|
document.execCommand("insertText", false, lastEmojiKeyword);
|
||||||
@@ -229,16 +231,16 @@ enabled(){
|
|||||||
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
|
this.currentSpanner.style.height = ($(this.currentKeyboard).height()-10)+"px";
|
||||||
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
|
$(".emoji-keyboard-popup-btn").parent().after(this.currentSpanner);
|
||||||
|
|
||||||
this.composePanelScroller.trigger("scroll");
|
this.getDrawerScroller().trigger("scroll");
|
||||||
};
|
};
|
||||||
|
|
||||||
const getKeyboardTop = () => {
|
const getKeyboardTop = () => {
|
||||||
let button = $(".emoji-keyboard-popup-btn");
|
let button = $(".emoji-keyboard-popup-btn");
|
||||||
return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
|
return button.offset().top + button.outerHeight() + me.getDrawerScroller().scrollTop() + 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertEmoji = (src, alt) => {
|
const insertEmoji = (src, alt) => {
|
||||||
let input = this.composeInput;
|
let input = this.getDrawerInput();
|
||||||
|
|
||||||
let val = input.val();
|
let val = input.val();
|
||||||
let posStart = input[0].selectionStart;
|
let posStart = input[0].selectionStart;
|
||||||
@@ -378,6 +380,15 @@ enabled(){
|
|||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.drawerToggleEvent = function(e, data){
|
||||||
|
if (data.activeDrawer === "compose"){
|
||||||
|
setTimeout(function(){
|
||||||
|
$(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent);
|
||||||
|
$(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.documentClickEvent = function(e){
|
this.documentClickEvent = function(e){
|
||||||
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
if (me.currentKeyboard && $(e.target).closest(".compose-text-container").length === 0){
|
||||||
hideKeyboard();
|
hideKeyboard();
|
||||||
@@ -396,20 +407,26 @@ enabled(){
|
|||||||
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
me.currentKeyboard.style.top = getKeyboardTop()+"px";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// re-enabling
|
||||||
|
|
||||||
|
let maybeDockedComposePanel = $(".js-docked-compose");
|
||||||
|
|
||||||
|
if (maybeDockedComposePanel.length){
|
||||||
|
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
|
||||||
|
this.drawerToggleEvent({}, { activeDrawer: "compose" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
this.composeDrawer = $("[data-drawer='compose']");
|
this.composeDrawer = $(".js-drawer[data-drawer='compose']");
|
||||||
this.composeInput = $(".js-compose-text", ".js-docked-compose");
|
|
||||||
this.composeSelector = ".js-compose-text,.js-reply-tweetbox";
|
this.composeSelector = ".js-compose-text,.js-reply-tweetbox";
|
||||||
|
|
||||||
this.composePanelScroller = $(".js-compose-scroller", ".js-docked-compose").first().children().first();
|
|
||||||
this.composePanelScroller.on("scroll", this.composerScrollEvent);
|
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").on("click", this.emojiKeyboardButtonClickEvent);
|
|
||||||
$(document).on("click", this.documentClickEvent);
|
$(document).on("click", this.documentClickEvent);
|
||||||
$(document).on("keydown", this.documentKeyEvent);
|
$(document).on("keydown", this.documentKeyEvent);
|
||||||
|
$(document).on("uiDrawerActive", this.drawerToggleEvent);
|
||||||
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).on("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
|
||||||
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
$(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
$(document).on("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
||||||
@@ -529,14 +546,13 @@ disabled(){
|
|||||||
$(this.currentSpanner).remove();
|
$(this.currentSpanner).remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.composePanelScroller.off("scroll", this.composerScrollEvent);
|
|
||||||
|
|
||||||
$(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
|
|
||||||
$(".emoji-keyboard-popup-btn").remove();
|
$(".emoji-keyboard-popup-btn").remove();
|
||||||
|
|
||||||
$(document).off("click", this.documentClickEvent);
|
$(document).off("click", this.documentClickEvent);
|
||||||
$(document).off("keydown", this.documentKeyEvent);
|
$(document).off("keydown", this.documentKeyEvent);
|
||||||
|
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
||||||
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
$(document).off("uiComposeImageAdded", this.uploadFilesEvent);
|
||||||
|
|
||||||
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);
|
||||||
|
|
||||||
$(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
$(document).off("keydown", this.composeSelector, this.composeInputKeyDownEvent);
|
||||||
|
@@ -383,7 +383,7 @@ enabled(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
ready(){
|
ready(){
|
||||||
$(".manage-templates-btn").on("click", this.manageTemplatesButtonClickEvent);
|
$(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
|
||||||
$(document).on("uiDrawerActive", this.drawerToggleEvent);
|
$(document).on("uiDrawerActive", this.drawerToggleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,6 +391,7 @@ disabled(){
|
|||||||
$(".manage-templates-btn").remove();
|
$(".manage-templates-btn").remove();
|
||||||
$("#templates-modal-wrap").remove();
|
$("#templates-modal-wrap").remove();
|
||||||
|
|
||||||
|
$(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
|
||||||
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
$(document).off("uiDrawerActive", this.drawerToggleEvent);
|
||||||
|
|
||||||
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
|
||||||
|
@@ -171,14 +171,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.template-editor-form input, .template-editor-form textarea {
|
.template-editor-form input, .template-editor-form textarea {
|
||||||
color: #111;
|
color: #111 !important;
|
||||||
background-color: #fff;
|
background-color: #fff !important;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-editor-form input:focus, .template-editor-form textarea:focus {
|
.template-editor-form input:focus, .template-editor-form textarea:focus {
|
||||||
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6);
|
box-shadow: inset 0 1px 3px rgba(17, 17, 17, 0.1), 0 0 8px rgba(80, 165, 230, 0.6) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.template-editor-form textarea {
|
.template-editor-form textarea {
|
||||||
|
249
Resources/PostBuild.fsx
Normal file
249
Resources/PostBuild.fsx
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Diagnostics
|
||||||
|
open System.IO
|
||||||
|
open System.Text.RegularExpressions
|
||||||
|
open System.Threading.Tasks
|
||||||
|
|
||||||
|
// "$(DevEnvDir)CommonExtensions\Microsoft\FSharp\fsi.exe" "$(ProjectDir)Resources\PostBuild.fsx" --exec --nologo -- "$(TargetDir)\" "$(ProjectDir)\" "$(ConfigurationName)"
|
||||||
|
// "$(ProjectDir)bld\post_build.exe" "$(TargetDir)\" "$(ProjectDir)\" "$(ConfigurationName)"
|
||||||
|
|
||||||
|
exception ArgumentUsage of string
|
||||||
|
|
||||||
|
#if !INTERACTIVE
|
||||||
|
[<EntryPoint>]
|
||||||
|
#endif
|
||||||
|
let main (argv: string[]) =
|
||||||
|
try
|
||||||
|
if argv.Length < 2 then
|
||||||
|
#if INTERACTIVE
|
||||||
|
raise (ArgumentUsage "fsi.exe PostBuild.fsx --exec --nologo -- <TargetDir> <ProjectDir> [ConfigurationName] [VersionTag]")
|
||||||
|
#else
|
||||||
|
raise (ArgumentUsage "PostBuild.exe <TargetDir> <ProjectDir> [ConfigurationName] [VersionTag]")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
let _time name func =
|
||||||
|
let sw = Stopwatch.StartNew()
|
||||||
|
func()
|
||||||
|
sw.Stop()
|
||||||
|
printfn "[%s took %i ms]" name (int (Math.Round(sw.Elapsed.TotalMilliseconds)))
|
||||||
|
|
||||||
|
let (+/) path1 path2 =
|
||||||
|
Path.Combine(path1, path2)
|
||||||
|
|
||||||
|
let sw = Stopwatch.StartNew()
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
let targetDir = argv.[0]
|
||||||
|
let projectDir = argv.[1]
|
||||||
|
|
||||||
|
let configuration = if argv.Length >= 3 then argv.[2]
|
||||||
|
else "Release"
|
||||||
|
|
||||||
|
let version = if argv.Length >= 4 then argv.[3]
|
||||||
|
else ((targetDir +/ "TweetDuck.exe") |> FileVersionInfo.GetVersionInfo).FileVersion
|
||||||
|
|
||||||
|
printfn "--------------------------"
|
||||||
|
printfn "TweetDuck version %s" version
|
||||||
|
printfn "--------------------------"
|
||||||
|
|
||||||
|
let localesDir = targetDir +/ "locales"
|
||||||
|
let scriptsDir = targetDir +/ "scripts"
|
||||||
|
let pluginsDir = targetDir +/ "plugins"
|
||||||
|
let importsDir = scriptsDir +/ "imports"
|
||||||
|
|
||||||
|
// Functions (Strings)
|
||||||
|
|
||||||
|
let filterNotEmpty =
|
||||||
|
Seq.filter (not << String.IsNullOrEmpty)
|
||||||
|
|
||||||
|
let replaceRegex (pattern: string) (replacement: string) input =
|
||||||
|
Regex.Replace(input, pattern, replacement)
|
||||||
|
|
||||||
|
let collapseLines separator (sequence: string seq) =
|
||||||
|
String.Join(separator, sequence)
|
||||||
|
|
||||||
|
let trimStart (line: string) =
|
||||||
|
line.TrimStart()
|
||||||
|
|
||||||
|
// Functions (File Management)
|
||||||
|
let copyFile source target =
|
||||||
|
File.Copy(source, target, true)
|
||||||
|
|
||||||
|
let createDirectory path =
|
||||||
|
Directory.CreateDirectory(path) |> ignore
|
||||||
|
|
||||||
|
let rec copyDirectoryContentsFiltered source target (filter: string -> bool) =
|
||||||
|
if not (Directory.Exists(target)) then
|
||||||
|
Directory.CreateDirectory(target) |> ignore
|
||||||
|
|
||||||
|
let src = DirectoryInfo(source)
|
||||||
|
|
||||||
|
for file in src.EnumerateFiles() do
|
||||||
|
if filter file.Name then
|
||||||
|
file.CopyTo(target +/ file.Name) |> ignore
|
||||||
|
|
||||||
|
for dir in src.EnumerateDirectories() do
|
||||||
|
if filter dir.Name then
|
||||||
|
copyDirectoryContentsFiltered dir.FullName (target +/ dir.Name) filter
|
||||||
|
|
||||||
|
let copyDirectoryContents source target =
|
||||||
|
copyDirectoryContentsFiltered source target (fun _ -> true)
|
||||||
|
|
||||||
|
// Functions (File Processing)
|
||||||
|
|
||||||
|
let byPattern path pattern =
|
||||||
|
Directory.EnumerateFiles(path, pattern, SearchOption.AllDirectories)
|
||||||
|
|
||||||
|
let exceptEndingWith name =
|
||||||
|
Seq.filter (fun (file: string) -> not (file.EndsWith(name)))
|
||||||
|
|
||||||
|
let iterateFiles (files: string seq) (func: string -> unit) =
|
||||||
|
Parallel.ForEach(files, func) |> ignore
|
||||||
|
|
||||||
|
let readFile file = seq {
|
||||||
|
use stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan)
|
||||||
|
use reader = new StreamReader(stream)
|
||||||
|
let mutable cont = true
|
||||||
|
|
||||||
|
while cont do
|
||||||
|
let line = reader.ReadLine()
|
||||||
|
|
||||||
|
if line = null then
|
||||||
|
cont <- false
|
||||||
|
else
|
||||||
|
yield line
|
||||||
|
}
|
||||||
|
|
||||||
|
let writeFile (fullPath: string) (lines: string seq) =
|
||||||
|
let relativePath = fullPath.[(targetDir.Length)..]
|
||||||
|
let includeVersion = relativePath.StartsWith(@"scripts\") && not (relativePath.StartsWith(@"scripts\imports\"))
|
||||||
|
let finalLines = if includeVersion then seq { yield "#" + version; yield! lines } else lines
|
||||||
|
|
||||||
|
File.WriteAllLines(fullPath, finalLines |> filterNotEmpty |> Seq.toArray)
|
||||||
|
printfn "Processed %s" relativePath
|
||||||
|
|
||||||
|
let processFiles (files: string seq) (extProcessors: IDictionary<string, (string seq -> string seq)>) =
|
||||||
|
let rec processFileContents file =
|
||||||
|
readFile file
|
||||||
|
|> extProcessors.[Path.GetExtension(file)]
|
||||||
|
|> Seq.map (fun line ->
|
||||||
|
Regex.Replace(line, @"#import ""(.*?)""", (fun matchInfo ->
|
||||||
|
processFileContents(importsDir +/ matchInfo.Groups.[1].Value.Trim())
|
||||||
|
|> collapseLines (Environment.NewLine)
|
||||||
|
|> (fun contents -> contents.TrimEnd())
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
iterateFiles files (fun file ->
|
||||||
|
processFileContents file
|
||||||
|
|> (writeFile file)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Build
|
||||||
|
|
||||||
|
copyFile (projectDir +/ "bld/Resources/LICENSES.txt") (targetDir +/ "LICENSES.txt")
|
||||||
|
|
||||||
|
copyDirectoryContents (projectDir +/ "Resources/Scripts") scriptsDir
|
||||||
|
|
||||||
|
createDirectory (pluginsDir +/ "official")
|
||||||
|
createDirectory (pluginsDir +/ "user")
|
||||||
|
|
||||||
|
copyDirectoryContentsFiltered
|
||||||
|
(projectDir +/ "Resources/Plugins")
|
||||||
|
(pluginsDir +/ "official")
|
||||||
|
(fun name -> name <> ".debug" && name <> "emoji-instructions.txt")
|
||||||
|
|
||||||
|
if configuration = "Debug" then
|
||||||
|
copyDirectoryContents
|
||||||
|
(projectDir +/ "Resources/Plugins/.debug")
|
||||||
|
(pluginsDir +/ "user/.debug")
|
||||||
|
|
||||||
|
if Directory.Exists(localesDir) || configuration = "Release" then
|
||||||
|
Directory.EnumerateFiles(localesDir, "*.pak")
|
||||||
|
|> exceptEndingWith @"\en-US.pak"
|
||||||
|
|> Seq.iter (File.Delete)
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
|
||||||
|
if File.ReadAllText(pluginsDir +/ "official/emoji-keyboard/emoji-ordering.txt").IndexOf('\r') <> -1 then
|
||||||
|
raise (FormatException("emoji-ordering.txt must not have any carriage return characters"))
|
||||||
|
else
|
||||||
|
printfn "Verified emoji-ordering.txt"
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
|
||||||
|
let fileProcessors =
|
||||||
|
dict [
|
||||||
|
".js", (fun (lines: string seq) ->
|
||||||
|
lines
|
||||||
|
|> Seq.map (fun line ->
|
||||||
|
line
|
||||||
|
|> trimStart
|
||||||
|
|> replaceRegex @"^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$" "$1"
|
||||||
|
|> replaceRegex @"(?<!\w)(return|throw)(\s.*?)? if (.*?);" "if ($3)$1$2;"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
".css", (fun (lines: string seq) ->
|
||||||
|
lines
|
||||||
|
|> Seq.map (fun line ->
|
||||||
|
line
|
||||||
|
|> replaceRegex @"\s*/\*.*?\*/" ""
|
||||||
|
|> replaceRegex @"^(\S.*) {$" "$1{"
|
||||||
|
|> replaceRegex @"^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$" "$1:$2$3;"
|
||||||
|
)
|
||||||
|
|> filterNotEmpty
|
||||||
|
|> collapseLines " "
|
||||||
|
|> replaceRegex @"([{};])\s" "$1"
|
||||||
|
|> replaceRegex @";}" "}"
|
||||||
|
|> Seq.singleton
|
||||||
|
);
|
||||||
|
|
||||||
|
".html", (fun (lines: string seq) ->
|
||||||
|
lines
|
||||||
|
|> Seq.map trimStart
|
||||||
|
);
|
||||||
|
|
||||||
|
".meta", (fun (lines: string seq) ->
|
||||||
|
lines
|
||||||
|
|> Seq.map (fun line -> line.Replace("{version}", version))
|
||||||
|
);
|
||||||
|
]
|
||||||
|
|
||||||
|
processFiles ((byPattern targetDir "*.js") |> exceptEndingWith @"\configuration.default.js") fileProcessors
|
||||||
|
processFiles (byPattern targetDir "*.css") fileProcessors
|
||||||
|
processFiles (byPattern targetDir "*.html") fileProcessors
|
||||||
|
processFiles (byPattern pluginsDir "*.meta") fileProcessors
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
|
||||||
|
Directory.Delete(importsDir, true)
|
||||||
|
|
||||||
|
// Finished
|
||||||
|
|
||||||
|
sw.Stop()
|
||||||
|
printfn "------------------"
|
||||||
|
printfn "Finished in %i ms" (int (Math.Round(sw.Elapsed.TotalMilliseconds)))
|
||||||
|
printfn "------------------"
|
||||||
|
0
|
||||||
|
|
||||||
|
with
|
||||||
|
| ArgumentUsage message ->
|
||||||
|
printfn ""
|
||||||
|
printfn "Build script usage:"
|
||||||
|
printfn "%s" message
|
||||||
|
printfn ""
|
||||||
|
1
|
||||||
|
| ex ->
|
||||||
|
printfn ""
|
||||||
|
printfn "Encountered an error while running PostBuild:"
|
||||||
|
printfn "%A" ex
|
||||||
|
printfn ""
|
||||||
|
1
|
||||||
|
|
||||||
|
#if INTERACTIVE
|
||||||
|
printfn "Running PostBuild in interpreter..."
|
||||||
|
main (fsi.CommandLineArgs |> Array.skip (1 + (fsi.CommandLineArgs |> Array.findIndex (fun arg -> arg = "--"))))
|
||||||
|
#endif
|
@@ -1,166 +0,0 @@
|
|||||||
Param(
|
|
||||||
[Parameter(Mandatory = $True, Position = 1)][string] $targetDir,
|
|
||||||
[Parameter(Mandatory = $True, Position = 2)][string] $projectDir,
|
|
||||||
[Parameter(Position = 3)][string] $configuration = "Release",
|
|
||||||
[Parameter(Position = 4)][string] $version = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
try{
|
|
||||||
$sw = [Diagnostics.Stopwatch]::StartNew()
|
|
||||||
|
|
||||||
if ($version.Equals("")){
|
|
||||||
$version = (Get-Item (Join-Path $targetDir "TweetDuck.exe")).VersionInfo.FileVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "--------------------------"
|
|
||||||
Write-Host "TweetDuck version" $version
|
|
||||||
Write-Host "--------------------------"
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
|
|
||||||
if (Test-Path (Join-Path $targetDir "locales")){
|
|
||||||
Remove-Item -Path (Join-Path $targetDir "locales\*.pak") -Exclude "en-US.pak"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy resources
|
|
||||||
|
|
||||||
Copy-Item (Join-Path $projectDir "bld\Resources\LICENSES.txt") -Destination $targetDir -Force
|
|
||||||
|
|
||||||
New-Item -ItemType directory -Path $targetDir -Name "scripts" | Out-Null
|
|
||||||
New-Item -ItemType directory -Path $targetDir -Name "plugins" | Out-Null
|
|
||||||
New-Item -ItemType directory -Path $targetDir -Name "plugins\official" | Out-Null
|
|
||||||
New-Item -ItemType directory -Path $targetDir -Name "plugins\user" | Out-Null
|
|
||||||
|
|
||||||
Copy-Item (Join-Path $projectDir "Resources\Scripts\*") -Recurse -Destination (Join-Path $targetDir "scripts")
|
|
||||||
Copy-Item (Join-Path $projectDir "Resources\Plugins\*") -Recurse -Destination (Join-Path $targetDir "plugins\official") -Exclude ".debug", "emoji-instructions.txt"
|
|
||||||
|
|
||||||
if ($configuration -eq "Debug"){
|
|
||||||
New-Item -ItemType directory -Path $targetDir -Name "plugins\user\.debug" | Out-Null
|
|
||||||
Copy-Item (Join-Path $projectDir "Resources\Plugins\.debug\*") -Recurse -Destination (Join-Path $targetDir "plugins\user\.debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Helper functions
|
|
||||||
|
|
||||||
function Remove-Empty-Lines{
|
|
||||||
Param([Parameter(Mandatory = $True, Position = 1)] $lines)
|
|
||||||
|
|
||||||
foreach($line in $lines){
|
|
||||||
if ($line -ne ''){
|
|
||||||
$line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Check-Carriage-Return{
|
|
||||||
Param([Parameter(Mandatory = $True, Position = 1)] $file)
|
|
||||||
|
|
||||||
if (!(Test-Path $file)){
|
|
||||||
Throw "$file does not exist"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((Get-Content -Path $file -Raw).Contains("`r")){
|
|
||||||
Throw "$file must not have any carriage return characters"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Verified" $file.Substring($targetDir.Length)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Rewrite-File{
|
|
||||||
Param([Parameter(Mandatory = $True, Position = 1)] $file,
|
|
||||||
[Parameter(Mandatory = $True, Position = 2)] $lines,
|
|
||||||
[Parameter(Mandatory = $True, Position = 3)] $imports)
|
|
||||||
|
|
||||||
$lines = Remove-Empty-Lines($lines)
|
|
||||||
$relativePath = $file.FullName.Substring($targetDir.Length)
|
|
||||||
|
|
||||||
foreach($line in $lines){
|
|
||||||
if ($line.Contains('#import ')){
|
|
||||||
$imports.Add($file.FullName)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($relativePath.StartsWith("scripts\")){
|
|
||||||
$lines = (,("#" + $version) + $lines)
|
|
||||||
}
|
|
||||||
|
|
||||||
[IO.File]::WriteAllLines($file.FullName, $lines)
|
|
||||||
Write-Host "Processed" $relativePath
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validation
|
|
||||||
|
|
||||||
Check-Carriage-Return(Join-Path $targetDir "plugins\official\emoji-keyboard\emoji-ordering.txt")
|
|
||||||
|
|
||||||
# Processing
|
|
||||||
|
|
||||||
$imports = New-Object "System.Collections.Generic.List[string]"
|
|
||||||
|
|
||||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.js" -Exclude "configuration.default.js" -Recurse){
|
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
|
||||||
$lines = $lines | ForEach-Object { $_.TrimStart() }
|
|
||||||
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
|
|
||||||
$lines = $lines -Replace '(?<!\w)(return|throw)(\s.*?)? if (.*?);', 'if ($3)$1$2;'
|
|
||||||
Rewrite-File $file $lines $imports
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.css" -Recurse){
|
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
|
||||||
$lines = $lines -Replace '\s*/\*.*?\*/', ''
|
|
||||||
$lines = $lines -Replace '^(\S.*) {$', '$1{'
|
|
||||||
$lines = $lines -Replace '^\s+(.+?):\s*(.+?)(?:\s*(!important))?;$', '$1:$2$3;'
|
|
||||||
$lines = @((Remove-Empty-Lines($lines)) -Join ' ')
|
|
||||||
$lines = $lines -Replace '([{};])\s', '$1'
|
|
||||||
$lines = $lines -Replace ';}', '}'
|
|
||||||
Rewrite-File $file $lines $imports
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($file in Get-ChildItem -Path $targetDir -Filter "*.html" -Recurse){
|
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
|
||||||
$lines = $lines | ForEach-Object { $_.TrimStart() }
|
|
||||||
Rewrite-File $file $lines $imports
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($file in Get-ChildItem -Path (Join-Path $targetDir "plugins") -Filter "*.meta" -Recurse){
|
|
||||||
$lines = [IO.File]::ReadLines($file.FullName)
|
|
||||||
$lines = $lines -Replace '\{version\}', $version
|
|
||||||
Rewrite-File $file $lines $imports
|
|
||||||
}
|
|
||||||
|
|
||||||
# Imports
|
|
||||||
|
|
||||||
$importFolder = Join-Path $targetDir "scripts\imports"
|
|
||||||
|
|
||||||
foreach($path in $imports){
|
|
||||||
$text = [IO.File]::ReadAllText($path)
|
|
||||||
$text = [Regex]::Replace($text, '#import "(.*?)"', {
|
|
||||||
$importPath = Join-Path $importFolder ($args[0].Groups[1].Value.Trim())
|
|
||||||
$importStr = [IO.File]::ReadAllText($importPath).TrimEnd()
|
|
||||||
|
|
||||||
if ($importStr[0] -eq '#'){
|
|
||||||
$importStr = $importStr.Substring($importStr.IndexOf("`n") + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return $importStr
|
|
||||||
}, [System.Text.RegularExpressions.RegexOptions]::MultiLine)
|
|
||||||
|
|
||||||
[IO.File]::WriteAllText($path, $text)
|
|
||||||
Write-Host "Resolved" $path.Substring($targetDir.Length)
|
|
||||||
}
|
|
||||||
|
|
||||||
[IO.Directory]::Delete($importFolder, $True)
|
|
||||||
|
|
||||||
# Finished
|
|
||||||
|
|
||||||
$sw.Stop()
|
|
||||||
Write-Host "------------------"
|
|
||||||
Write-Host "Finished in" $([math]::Round($sw.Elapsed.TotalMilliseconds)) "ms"
|
|
||||||
Write-Host "------------------"
|
|
||||||
|
|
||||||
}catch{
|
|
||||||
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
|
|
||||||
Write-Host $_
|
|
||||||
Exit 1
|
|
||||||
}
|
|
@@ -10,10 +10,11 @@ try{
|
|||||||
$sharpMatch = Select-String -Path $mainProj '<Import Project="packages\\CefSharp\.Common\.(.*?)\\'
|
$sharpMatch = Select-String -Path $mainProj '<Import Project="packages\\CefSharp\.Common\.(.*?)\\'
|
||||||
$sharpVersion = $sharpMatch.Matches[0].Groups[1].Value
|
$sharpVersion = $sharpMatch.Matches[0].Groups[1].Value
|
||||||
|
|
||||||
$propsFiles = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.props",
|
$replaceWhenTag = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.props",
|
||||||
"..\packages\CefSharp.WinForms.${sharpVersion}\build\CefSharp.WinForms.props"
|
"..\packages\CefSharp.WinForms.${sharpVersion}\build\CefSharp.WinForms.props",
|
||||||
|
"..\packages\CefSharp.WinForms.${sharpVersion}\build\CefSharp.WinForms.targets"
|
||||||
|
|
||||||
$targetFiles = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.targets"
|
$replaceItemGroupTag = "..\packages\CefSharp.Common.${sharpVersion}\build\CefSharp.Common.targets"
|
||||||
|
|
||||||
# Greetings
|
# Greetings
|
||||||
|
|
||||||
@@ -39,14 +40,14 @@ try{
|
|||||||
|
|
||||||
Write-Host "Removing x64 and AnyCPU from package files..."
|
Write-Host "Removing x64 and AnyCPU from package files..."
|
||||||
|
|
||||||
foreach($file in $propsFiles){
|
foreach($file in $replaceWhenTag){
|
||||||
$contents = [IO.File]::ReadAllText($file)
|
$contents = [IO.File]::ReadAllText($file)
|
||||||
$contents = $contents -Replace '(?<=<When Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
|
$contents = $contents -Replace '(?<=<When Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
|
||||||
|
|
||||||
[IO.File]::WriteAllText($file, $contents)
|
[IO.File]::WriteAllText($file, $contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($file in $targetFiles){
|
foreach($file in $replaceItemGroupTag){
|
||||||
$contents = [IO.File]::ReadAllText($file)
|
$contents = [IO.File]::ReadAllText($file)
|
||||||
$contents = $contents -Replace '(?<=<ItemGroup Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
|
$contents = $contents -Replace '(?<=<ItemGroup Condition=")(''\$\(Platform\)'' == ''(AnyCPU|x64)'')(?=">)', 'false'
|
||||||
|
|
||||||
|
@@ -80,6 +80,10 @@ namespace TweetDuck.Resources{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ClearCache(){
|
||||||
|
CachedData.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private static void ShowLoadError(Control sync, string message){
|
private static void ShowLoadError(Control sync, string message){
|
||||||
sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
|
sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
|
||||||
}
|
}
|
||||||
@@ -87,7 +91,7 @@ namespace TweetDuck.Resources{
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
|
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
|
||||||
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
|
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
|
||||||
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "Resources", "PostBuild.ps1");
|
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "bld", "post_build.exe");
|
||||||
|
|
||||||
static ScriptLoader(){
|
static ScriptLoader(){
|
||||||
if (File.Exists(HotSwapRebuildScript)){
|
if (File.Exists(HotSwapRebuildScript)){
|
||||||
@@ -110,8 +114,8 @@ namespace TweetDuck.Resources{
|
|||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
using(Process process = Process.Start(new ProcessStartInfo{
|
using(Process process = Process.Start(new ProcessStartInfo{
|
||||||
FileName = "powershell",
|
FileName = HotSwapRebuildScript,
|
||||||
Arguments = $"-ExecutionPolicy Unrestricted -File \"{HotSwapRebuildScript}\" \"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
|
Arguments = $"\"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
|
||||||
WindowStyle = ProcessWindowStyle.Hidden
|
WindowStyle = ProcessWindowStyle.Hidden
|
||||||
})){
|
})){
|
||||||
// ReSharper disable once PossibleNullReferenceException
|
// ReSharper disable once PossibleNullReferenceException
|
||||||
@@ -128,7 +132,7 @@ namespace TweetDuck.Resources{
|
|||||||
sw.Stop();
|
sw.Stop();
|
||||||
Debug.WriteLine("Finished rebuild script in "+sw.ElapsedMilliseconds+" ms");
|
Debug.WriteLine("Finished rebuild script in "+sw.ElapsedMilliseconds+" ms");
|
||||||
|
|
||||||
CachedData.Clear();
|
ClearCache();
|
||||||
|
|
||||||
// Force update plugin manager setup scripts
|
// Force update plugin manager setup scripts
|
||||||
|
|
||||||
|
@@ -1,14 +1,4 @@
|
|||||||
(function($TD, $TDX, $, TD){
|
(function($TD, $TDX, $, TD){
|
||||||
//
|
|
||||||
// Variable: Current highlighted column jQuery & data objects.
|
|
||||||
//
|
|
||||||
let highlightedColumnEle, highlightedColumnObj;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Variable: Currently highlighted tweet jQuery & data objects.
|
|
||||||
//
|
|
||||||
let highlightedTweetEle, highlightedTweetObj;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Variable: Array of functions called after the website app is loaded.
|
// Variable: Array of functions called after the website app is loaded.
|
||||||
//
|
//
|
||||||
@@ -111,6 +101,53 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Returns an object containing data about the column below the cursor.
|
||||||
|
//
|
||||||
|
const getHoveredColumn = function(){
|
||||||
|
let hovered = document.querySelectorAll(":hover");
|
||||||
|
|
||||||
|
for(let index = hovered.length-1; index >= 0; index--){
|
||||||
|
let ele = hovered[index];
|
||||||
|
|
||||||
|
if (ele.tagName === "SECTION" && ele.classList.contains("js-column")){
|
||||||
|
let obj = TD.controller.columnManager.get(ele.getAttribute("data-column"));
|
||||||
|
|
||||||
|
if (obj){
|
||||||
|
return { ele, obj };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Function: Returns an object containing data about the tweet below the cursor.
|
||||||
|
//
|
||||||
|
const getHoveredTweet = function(){
|
||||||
|
let hovered = document.querySelectorAll(":hover");
|
||||||
|
|
||||||
|
for(let index = hovered.length-1; index >= 0; index--){
|
||||||
|
let ele = hovered[index];
|
||||||
|
|
||||||
|
if (ele.tagName === "ARTICLE" && ele.classList.contains("js-stream-item") && ele.hasAttribute("data-account-key")){
|
||||||
|
let column = getHoveredColumn();
|
||||||
|
|
||||||
|
if (column){
|
||||||
|
let wrap = column.obj.findChirp(ele.getAttribute("data-key"));
|
||||||
|
let obj = column.obj.findChirp(ele.getAttribute("data-tweet-id")) || wrap;
|
||||||
|
|
||||||
|
if (obj){
|
||||||
|
return { ele, obj, wrap, column };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Function: Retrieves a property of an element with a specified class.
|
// Function: Retrieves a property of an element with a specified class.
|
||||||
//
|
//
|
||||||
@@ -208,6 +245,7 @@
|
|||||||
if (column.model.getHasNotification()){
|
if (column.model.getHasNotification()){
|
||||||
let sensitive = isSensitive(tweet);
|
let sensitive = isSensitive(tweet);
|
||||||
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
let previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
||||||
|
// TODO new cards don't have either previews or links
|
||||||
|
|
||||||
let html = $(tweet.render({
|
let html = $(tweet.render({
|
||||||
withFooter: false,
|
withFooter: false,
|
||||||
@@ -280,6 +318,7 @@
|
|||||||
html.children().first().addClass("td-notification-padded");
|
html.children().first().addClass("td-notification-padded");
|
||||||
}
|
}
|
||||||
else if (type.includes("list_member")){
|
else if (type.includes("list_member")){
|
||||||
|
html.children().first().addClass("td-notification-padded td-notification-padded-alt");
|
||||||
html.find(".activity-header").css("margin-top", "2px");
|
html.find(".activity-header").css("margin-top", "2px");
|
||||||
html.find(".avatar").first().css("margin-bottom", "0");
|
html.find(".avatar").first().css("margin-bottom", "0");
|
||||||
}
|
}
|
||||||
@@ -375,7 +414,7 @@
|
|||||||
let fontSizeName = TD.settings.getFontSize();
|
let fontSizeName = TD.settings.getFontSize();
|
||||||
let themeName = TD.settings.getTheme();
|
let themeName = TD.settings.getTheme();
|
||||||
|
|
||||||
let columnBackground = getClassStyleProperty("column", "background-color");
|
let columnBackground = getClassStyleProperty("column-panel", "background-color");
|
||||||
|
|
||||||
let tags = [
|
let tags = [
|
||||||
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
|
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
|
||||||
@@ -536,6 +575,31 @@
|
|||||||
data.setData("text/html", `<a href="${url}">${url}</a>`);
|
data.setData("text/html", `<a href="${url}">${url}</a>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (ensurePropertyExists(TD, "services", "TwitterStatus", "prototype", "_generateHTMLText")){
|
||||||
|
TD.services.TwitterStatus.prototype._generateHTMLText = prependToFunction(TD.services.TwitterStatus.prototype._generateHTMLText, function(){
|
||||||
|
let card = this.card;
|
||||||
|
let entities = this.entities;
|
||||||
|
return if !(card && entities);
|
||||||
|
|
||||||
|
let urls = entities.urls;
|
||||||
|
return if !(urls && urls.length);
|
||||||
|
|
||||||
|
let shortUrl = card.url;
|
||||||
|
let urlObj = entities.urls.find(obj => obj.url === shortUrl && obj.expanded_url);
|
||||||
|
|
||||||
|
if (urlObj){
|
||||||
|
let expandedUrl = urlObj.expanded_url;
|
||||||
|
card.url = expandedUrl;
|
||||||
|
|
||||||
|
let values = card.binding_values;
|
||||||
|
|
||||||
|
if (values && values.card_url){
|
||||||
|
values.card_url.string_value = expandedUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
|
||||||
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
|
||||||
|
|
||||||
@@ -625,8 +689,11 @@
|
|||||||
$(document.body).delegate("a", "contextmenu", function(){
|
$(document.body).delegate("a", "contextmenu", function(){
|
||||||
let me = $(this)[0];
|
let me = $(this)[0];
|
||||||
|
|
||||||
if (me.classList.contains("js-media-image-link") && highlightedTweetObj){
|
if (me.classList.contains("js-media-image-link")){
|
||||||
let tweet = highlightedTweetObj.hasMedia() ? highlightedTweetObj : highlightedTweetObj.quotedTweet;
|
let hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
|
let tweet = hovered.obj.hasMedia() ? hovered.obj : hovered.obj.quotedTweet;
|
||||||
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
let media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
||||||
|
|
||||||
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
||||||
@@ -639,7 +706,7 @@
|
|||||||
else if (me.classList.contains("js-gif-play")){
|
else if (me.classList.contains("js-gif-play")){
|
||||||
$TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
$TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
||||||
}
|
}
|
||||||
else{
|
else if (me.hasAttribute("data-full-url")){
|
||||||
$TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
|
$TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -677,65 +744,37 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Update highlighted column and tweet for context menu and other functionality.
|
// Block: Add tweet-related options to the context menu.
|
||||||
//
|
//
|
||||||
execSafe(function setupHighlightedColumn(){
|
execSafe(function setupTweetContextMenu(){
|
||||||
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "get");
|
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "get");
|
||||||
throw 2 if !ensurePropertyExists(TD, "services", "ChirpBase", "TWEET");
|
throw 2 if !ensurePropertyExists(TD, "services", "ChirpBase", "TWEET");
|
||||||
|
throw 3 if !ensurePropertyExists(TD, "services", "TwitterActionFollow");
|
||||||
const updateHighlightedColumn = function(ele){
|
|
||||||
highlightedColumnEle = ele;
|
|
||||||
highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
|
|
||||||
return !!highlightedColumnObj;
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateHighlightedTweet = function(ele, obj){
|
|
||||||
highlightedTweetEle = ele;
|
|
||||||
highlightedTweetObj = obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
const processMedia = function(chirp){
|
const processMedia = function(chirp){
|
||||||
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
|
||||||
};
|
};
|
||||||
|
|
||||||
app.delegate("section.js-column", {
|
app.delegate("section.js-column", {
|
||||||
mouseenter: function(){
|
|
||||||
if (!highlightedColumnObj){
|
|
||||||
updateHighlightedColumn($(this));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseleave: function(){
|
|
||||||
updateHighlightedColumn(null);
|
|
||||||
},
|
|
||||||
|
|
||||||
contextmenu: function(){
|
contextmenu: function(){
|
||||||
let tweet = highlightedTweetObj;
|
let hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
if (tweet && tweet.chirpType === TD.services.ChirpBase.TWEET){
|
let tweet = hovered.obj;
|
||||||
|
let quote = tweet.quotedTweet;
|
||||||
|
|
||||||
|
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
||||||
let tweetUrl = tweet.getChirpURL();
|
let tweetUrl = tweet.getChirpURL();
|
||||||
let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
|
let quoteUrl = quote && quote.getChirpURL();
|
||||||
let chirpAuthors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
|
||||||
let chirpImages = tweet.hasImage() ? processMedia(tweet) : tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : "";
|
let chirpAuthors = quote ? [ tweet.getMainUser().screenName, quote.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
||||||
|
let chirpImages = tweet.hasImage() ? processMedia(tweet) : quote && quote.hasImage() ? processMedia(quote) : "";
|
||||||
|
|
||||||
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
||||||
}
|
}
|
||||||
}
|
else if (tweet instanceof TD.services.TwitterActionFollow){
|
||||||
});
|
$TD.setRightClickedLink("link", tweet.following.getProfileURL());
|
||||||
|
}
|
||||||
app.delegate("article.js-stream-item", {
|
|
||||||
mouseenter: function(){
|
|
||||||
let me = $(this);
|
|
||||||
return if !me[0].hasAttribute("data-account-key") || (!highlightedColumnObj && !updateHighlightedColumn(me.closest("section.js-column")));
|
|
||||||
|
|
||||||
let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
|
|
||||||
return if !tweet;
|
|
||||||
|
|
||||||
updateHighlightedTweet(me, tweet);
|
|
||||||
},
|
|
||||||
|
|
||||||
mouseleave: function(){
|
|
||||||
updateHighlightedTweet(null, null);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -745,20 +784,20 @@
|
|||||||
//
|
//
|
||||||
execSafe(function setupTweetScreenshot(){
|
execSafe(function setupTweetScreenshot(){
|
||||||
window.TDGF_triggerScreenshot = function(){
|
window.TDGF_triggerScreenshot = function(){
|
||||||
return if !highlightedTweetObj || !highlightedColumnObj;
|
let hovered = getHoveredTweet();
|
||||||
|
return if !hovered;
|
||||||
|
|
||||||
let chirp = highlightedColumnObj.findChirp(highlightedTweetEle.attr("data-key")) || highlightedTweetObj;
|
let columnWidth = $(hovered.column.ele).width();
|
||||||
|
let tweet = hovered.wrap || hovered.obj;
|
||||||
|
|
||||||
let columnWidth = highlightedColumnEle.width();
|
let html = $(tweet.render({
|
||||||
|
|
||||||
let html = $(chirp.render({
|
|
||||||
withFooter: false,
|
withFooter: false,
|
||||||
withTweetActions: false,
|
withTweetActions: false,
|
||||||
isInConvo: false,
|
isInConvo: false,
|
||||||
isFavorite: false,
|
isFavorite: false,
|
||||||
isRetweeted: false, // keeps retweet mark above tweet
|
isRetweeted: false, // keeps retweet mark above tweet
|
||||||
isPossiblySensitive: false,
|
isPossiblySensitive: false,
|
||||||
mediaPreviewSize: highlightedColumnObj.getMediaPreviewSize()
|
mediaPreviewSize: hovered.column.obj.getMediaPreviewSize()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||||
@@ -786,12 +825,12 @@
|
|||||||
let gif = html.find(".js-media-gif-container");
|
let gif = html.find(".js-media-gif-container");
|
||||||
|
|
||||||
if (gif.length){
|
if (gif.length){
|
||||||
gif.css("background-image", 'url("'+chirp.getMedia()[0].small()+'")');
|
gif.css("background-image", 'url("'+tweet.getMedia()[0].small()+'")');
|
||||||
}
|
}
|
||||||
|
|
||||||
let type = chirp.getChirpType();
|
let type = tweet.getChirpType();
|
||||||
|
|
||||||
if ((type.startsWith("favorite") || type.startsWith("retweet")) && chirp.isAboutYou()){
|
if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
|
||||||
html.addClass("td-notification-padded");
|
html.addClass("td-notification-padded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,10 +899,11 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const tryCloseHighlightedColumn = function(){
|
const tryCloseHighlightedColumn = function(){
|
||||||
if (highlightedColumnEle){
|
let column = getHoveredColumn();
|
||||||
let column = highlightedColumnEle.closest(".js-column");
|
return false if !column;
|
||||||
return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
|
|
||||||
}
|
let ele = $(column.ele);
|
||||||
|
return ((ele.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", ele)) || (ele.is(".is-shifted-1") && tryClickSelector(".js-column-back", ele)));
|
||||||
};
|
};
|
||||||
|
|
||||||
window.TDGF_onMouseClickExtra = function(button){
|
window.TDGF_onMouseClickExtra = function(button){
|
||||||
@@ -879,8 +919,10 @@
|
|||||||
$(".is-shifted-1 .js-column-back").click();
|
$(".is-shifted-1 .js-column-back").click();
|
||||||
}
|
}
|
||||||
else if (button === 2){ // forward button
|
else if (button === 2){ // forward button
|
||||||
if (highlightedTweetEle){
|
let hovered = getHoveredTweet();
|
||||||
highlightedTweetEle.children().first().click();
|
|
||||||
|
if (hovered){
|
||||||
|
$(hovered.ele).children().first().click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -997,14 +1039,20 @@
|
|||||||
// Block: Refocus the textbox after switching accounts.
|
// Block: Refocus the textbox after switching accounts.
|
||||||
//
|
//
|
||||||
onAppReady.push(function setupAccountSwitchRefocus(){
|
onAppReady.push(function setupAccountSwitchRefocus(){
|
||||||
const composeInput = $$(".js-compose-text", ".js-docked-compose");
|
|
||||||
|
|
||||||
const refocusInput = function(){
|
const refocusInput = function(){
|
||||||
composeInput.focus();
|
$$(".js-compose-text", ".js-docked-compose").focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", function(e){
|
const accountItemClickEvent = function(e){
|
||||||
setTimeout(refocusInput, 0);
|
setTimeout(refocusInput, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).on("uiDrawerActive", function(e, data){
|
||||||
|
if (data.activeDrawer === "compose"){
|
||||||
|
setTimeout(function(){
|
||||||
|
$$(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", accountItemClickEvent);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1156,6 +1204,8 @@
|
|||||||
//
|
//
|
||||||
execSafe(function setupVideoPlayer(){
|
execSafe(function setupVideoPlayer(){
|
||||||
window.TDGF_playVideo = function(url, username){
|
window.TDGF_playVideo = function(url, username){
|
||||||
|
return if !url;
|
||||||
|
|
||||||
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
|
||||||
$TD.playVideo(null, null);
|
$TD.playVideo(null, null);
|
||||||
}).appendTo(app);
|
}).appendTo(app);
|
||||||
@@ -1182,7 +1232,8 @@
|
|||||||
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
|
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
|
||||||
|
|
||||||
if (src){
|
if (src){
|
||||||
window.TDGF_playVideo(src, getUsername(highlightedTweetObj));
|
let hovered = getHoveredTweet();
|
||||||
|
window.TDGF_playVideo(src, getUsername(hovered && hovered.obj));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$TD.openBrowser(getVideoTweetLink($(this)));
|
$TD.openBrowser(getVideoTweetLink($(this)));
|
||||||
@@ -1460,6 +1511,8 @@
|
|||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
|
if (ensurePropertyExists(TD, "components", "ConversationDetailView", "prototype", "showChirp")){
|
||||||
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
|
TD.components.ConversationDetailView.prototype.showChirp = appendToFunction(TD.components.ConversationDetailView.prototype.showChirp, function(){
|
||||||
|
return if !$TDX.focusDmInput;
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
$(".js-reply-tweetbox").first().focus();
|
$(".js-reply-tweetbox").first().focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -1469,16 +1522,53 @@
|
|||||||
//
|
//
|
||||||
// Block: Fix DM notifications not showing if the conversation is open.
|
// Block: Fix DM notifications not showing if the conversation is open.
|
||||||
//
|
//
|
||||||
if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "getUnreadChirps")){
|
if (ensurePropertyExists(TD, "vo", "Column", "prototype", "mergeMissingChirps")){
|
||||||
const prevFunc = TD.services.TwitterConversation.prototype.getUnreadChirps;
|
TD.vo.Column.prototype.mergeMissingChirps = prependToFunction(TD.vo.Column.prototype.mergeMissingChirps, function(e){
|
||||||
|
let model = this.model;
|
||||||
|
|
||||||
TD.services.TwitterConversation.prototype.getUnreadChirps = function(e){
|
if (model && model.state && model.state.type === "privateMe" && !this.notificationsDisabled && e.poller.feed.managed){
|
||||||
return (e && e.sortIndex && !e.id && !this.notificationsDisabled)
|
let unread = [];
|
||||||
? this.messages.filter(t => t.chirpType === TD.services.ChirpBase.MESSAGE && !t.isOwnChirp() && !t.read && !t.belongsBelow(e)) // changed from belongsAbove
|
|
||||||
: prevFunc.apply(this, arguments);
|
for(let chirp of e.chirps){
|
||||||
};
|
if (Array.isArray(chirp.messages)){
|
||||||
|
Array.prototype.push.apply(unread, chirp.messages.filter(message => message.read === false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unread.length > 0){
|
||||||
|
if (ensurePropertyExists(TD, "util", "chirpReverseColumnSort")){
|
||||||
|
unread.sort(TD.util.chirpReverseColumnSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let message of unread){
|
||||||
|
onNewTweet(this, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO sound notifications are borked as well
|
||||||
|
// TODO figure out what to do with missed notifications at startup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Block: Fix DMs not being marked as read when replying to them.
|
||||||
|
//
|
||||||
|
execSafe(function markRepliedDMsAsRead(){
|
||||||
|
throw 1 if !ensurePropertyExists(TD, "controller", "clients", "getClient");
|
||||||
|
throw 2 if !ensurePropertyExists(TD, "services", "Conversations", "prototype", "getConversation");
|
||||||
|
|
||||||
|
$(document).on("dataDmSent", function(e, data){
|
||||||
|
let client = TD.controller.clients.getClient(data.request.accountKey);
|
||||||
|
return if !client;
|
||||||
|
|
||||||
|
let conversation = client.conversations.getConversation(data.request.conversationId);
|
||||||
|
return if !conversation;
|
||||||
|
|
||||||
|
conversation.markAsRead();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Limit amount of loaded DMs to avoid massive lag from re-opening them several times.
|
// Block: Limit amount of loaded DMs to avoid massive lag from re-opening them several times.
|
||||||
//
|
//
|
||||||
@@ -1555,79 +1645,12 @@
|
|||||||
// Block: Custom reload function with memory cleanup.
|
// Block: Custom reload function with memory cleanup.
|
||||||
//
|
//
|
||||||
window.TDGF_reload = function(){
|
window.TDGF_reload = function(){
|
||||||
try{
|
window.gc && window.gc();
|
||||||
let session = TD.storage.feedController.getAll()
|
window.location.reload();
|
||||||
.filter(feed => !!feed.getTopSortIndex())
|
|
||||||
.reduce((obj, feed) => (obj[feed.privateState.key] = feed.getTopSortIndex(), obj), {});
|
|
||||||
|
|
||||||
$TD.setSessionData("gc", JSON.stringify(session)).then(() => {
|
|
||||||
window.gc && window.gc();
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
}catch(err){
|
|
||||||
$TD.crashDebug("Error saving session during a reload");
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
|
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
|
||||||
};
|
};
|
||||||
|
|
||||||
if (window.TD_SESSION && window.TD_SESSION.gc){
|
|
||||||
let state;
|
|
||||||
|
|
||||||
try{
|
|
||||||
state = JSON.parse(window.TD_SESSION.gc);
|
|
||||||
}catch(err){
|
|
||||||
$TD.crashDebug("Invalid session gc data: "+window.TD_SESSION.gc);
|
|
||||||
state = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const showMissedNotifications = function(){
|
|
||||||
let tweets = [];
|
|
||||||
let columns = {};
|
|
||||||
|
|
||||||
let tmp = new TD.services.ChirpBase;
|
|
||||||
|
|
||||||
for(let column of Object.values(TD.controller.columnManager.getAll())){
|
|
||||||
for(let feed of column.getFeeds()){
|
|
||||||
if (feed.privateState.key in state){
|
|
||||||
tmp.sortIndex = state[feed.privateState.key];
|
|
||||||
|
|
||||||
for(let tweet of [].concat.apply([], column.updateArray.map(function(chirp){
|
|
||||||
return chirp.getUnreadChirps(tmp);
|
|
||||||
}))){
|
|
||||||
tweets.push(tweet);
|
|
||||||
columns[tweet.id] = column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tweets.sort(TD.util.chirpReverseColumnSort);
|
|
||||||
|
|
||||||
for(let tweet of tweets){
|
|
||||||
onNewTweet(columns[tweet.id], tweet);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
execSafe(function showMissedNotifications(){
|
|
||||||
throw 1 if !ensurePropertyExists(TD, "controller", "columnManager", "getAll");
|
|
||||||
|
|
||||||
$(document).one("dataColumnsLoaded", function(){
|
|
||||||
let columns = Object.values(TD.controller.columnManager.getAll());
|
|
||||||
let remaining = columns.length;
|
|
||||||
|
|
||||||
for(let column of columns){
|
|
||||||
column.ui.getChirpContainer().one("dataColumnFeedUpdated", () => {
|
|
||||||
if (--remaining === 0){
|
|
||||||
setTimeout(showMissedNotifications, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Disable default TweetDeck update notification.
|
// Block: Disable default TweetDeck update notification.
|
||||||
//
|
//
|
||||||
@@ -1662,8 +1685,6 @@
|
|||||||
onAppReady.forEach(func => execSafe(func));
|
onAppReady.forEach(func => execSafe(func));
|
||||||
onAppReady = null;
|
onAppReady = null;
|
||||||
|
|
||||||
delete window.TD_SESSION;
|
|
||||||
|
|
||||||
if (window.TD_PLUGINS){
|
if (window.TD_PLUGINS){
|
||||||
window.TD_PLUGINS.onReady();
|
window.TD_PLUGINS.onReady();
|
||||||
}
|
}
|
||||||
|
@@ -7,19 +7,30 @@
|
|||||||
min-width: 515px;
|
min-width: 515px;
|
||||||
max-width: 835px;
|
max-width: 835px;
|
||||||
height: 328px;
|
height: 328px;
|
||||||
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#td-introduction-modal .mdl-inner {
|
#td-introduction-modal .mdl-header {
|
||||||
padding-top: 0;
|
color: #8899a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
#td-introduction-modal .mdl-header-title {
|
#td-introduction-modal .mdl-header-title {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#td-introduction-modal .mdl-dismiss {
|
||||||
|
color: #292f33;
|
||||||
|
}
|
||||||
|
|
||||||
|
#td-introduction-modal .mdl-inner {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#td-introduction-modal .mdl-content {
|
#td-introduction-modal .mdl-content {
|
||||||
padding: 4px 16px 0;
|
padding: 4px 16px 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
border-color: #ccd6dd;
|
||||||
|
background: #eaeaea;
|
||||||
}
|
}
|
||||||
|
|
||||||
#td-introduction-modal p {
|
#td-introduction-modal p {
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
/*****************************/
|
|
||||||
/* Fix min width and margins */
|
|
||||||
/*****************************/
|
|
||||||
|
|
||||||
.page-canvas {
|
|
||||||
width: auto !important;
|
|
||||||
max-width: 888px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signout-wrapper {
|
|
||||||
width: auto !important;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.signout {
|
|
||||||
margin: 60px 0 54px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons {
|
|
||||||
padding-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************/
|
|
||||||
/* General styling */
|
|
||||||
/*******************/
|
|
||||||
|
|
||||||
.aside {
|
|
||||||
/* hide elements around dialog */
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons button, .buttons a {
|
|
||||||
/* style buttons */
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 4px !important;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3) !important;
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
@@ -121,16 +121,14 @@
|
|||||||
//
|
//
|
||||||
// Block: Setup a skip button.
|
// Block: Setup a skip button.
|
||||||
//
|
//
|
||||||
if (!document.body.hasAttribute("td-example-notification")){
|
document.body.children[0].insertAdjacentHTML("beforeend", `
|
||||||
document.body.children[0].insertAdjacentHTML("beforeend", `
|
|
||||||
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
|
<svg id="td-skip" width="10" height="17" viewBox="0 0 350 600">
|
||||||
<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
|
<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">
|
||||||
</svg>`);
|
</svg>`);
|
||||||
|
|
||||||
document.getElementById("td-skip").addEventListener("click", function(){
|
document.getElementById("td-skip").addEventListener("click", function(){
|
||||||
$TD.loadNextNotification();
|
$TD.loadNextNotification();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Block: Setup a hover class on body.
|
// Block: Setup a hover class on body.
|
||||||
|
@@ -38,19 +38,23 @@
|
|||||||
/* Square-ify stuff */
|
/* Square-ify stuff */
|
||||||
/********************/
|
/********************/
|
||||||
|
|
||||||
button, .btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
|
#tduck .compose-media-bar-remove .icon-close, #tduck .compose-media-grid-remove .icon-close {
|
||||||
|
border-radius: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, .btn, .mdl, .mdl-content, .modal-content, .popover, .lst-modal, .tooltip-inner {
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-item, .media-preview, .media-image, .js-media-added .br--4, #tduck .compose-message-recipient img {
|
.media-item, .media-preview, .media-image, .media-badge, .js-media-sensitive-overlay, .js-media-added .br--4, #tduck .compose-message-recipient img {
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-button, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder, .detail-view-inline-text {
|
.tweet-button, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .compose-media-bar-thumb, .media-grid-container, .js-add-image-description, .js-quote-tweet-holder, .detail-view-inline-text, .mdl-column-rhs {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu, .list-item-last, .quoted-tweet, input, textarea, select, .prf-header {
|
.dropdown-menu, .list-item-last, .quoted-tweet, .hw-card-container > div, input, textarea, select, .prf-header {
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,23 +178,46 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
|
|||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .has-source-avatar .item-img, .stream-item[data-key^="retweet"] .has-source-avatar .item-img {
|
.stream-item[data-key^="favorite"] .has-source-avatar .item-img,
|
||||||
|
.stream-item[data-key^="retweet"] .has-source-avatar .item-img {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 21px;
|
left: 21px;
|
||||||
top: 48px;
|
top: 48px;
|
||||||
width: 0 !important;
|
width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc {
|
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc,
|
||||||
|
.stream-item[data-key^="retweet"] .has-source-avatar > .nbfc {
|
||||||
margin-left: 46px;
|
margin-left: 46px;
|
||||||
line-height: unset !important;
|
line-height: unset !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc > .avatar, .stream-item[data-key^="retweet"] .has-source-avatar > .nbfc > .avatar {
|
.stream-item[data-key^="favorite"] .has-source-avatar > .nbfc > .avatar,
|
||||||
|
.stream-item[data-key^="retweet"] .has-source-avatar > .nbfc > .avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
margin-left: -34px;
|
margin-left: -34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stream-item[data-key^="list_"] .activity-header .item-img {
|
||||||
|
position: relative;
|
||||||
|
left: 10px;
|
||||||
|
top: 2px;
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-item[data-key^="list_"] .activity-header > .nbfc {
|
||||||
|
margin-left: 46px;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
min-height: 30px !important;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-item[data-key^="list_"] .activity-header > .nbfc > .avatar {
|
||||||
|
position: absolute;
|
||||||
|
margin-left: -34px;
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************/
|
/***********************/
|
||||||
/* Tweaks for features */
|
/* Tweaks for features */
|
||||||
/***********************/
|
/***********************/
|
||||||
@@ -230,9 +257,9 @@ a[data-full-url] {
|
|||||||
bottom: 0 !important;
|
bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************************/
|
/**************************************************************************/
|
||||||
/* Prevent column icons from being hidden by column title */
|
/* Prevent column icons from being hidden by column title & fix alignment */
|
||||||
/**********************************************************/
|
/**************************************************************************/
|
||||||
|
|
||||||
.column-header-title {
|
.column-header-title {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
@@ -247,9 +274,10 @@ a[data-full-url] {
|
|||||||
|
|
||||||
.column-header-links {
|
.column-header-links {
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-td-icon="icon-message"] .column-header-links {
|
.column[data-td-icon="icon-message"] .column-header-links {
|
||||||
min-width: 86px;
|
min-width: 86px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,14 +369,25 @@ html[data-td-font='smallest'] .badge-verified:before {
|
|||||||
|
|
||||||
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
|
||||||
/* fix cut off badge in detail view */
|
/* fix cut off badge in detail view */
|
||||||
width: 13px !important;
|
width: 14px !important;
|
||||||
height: 14px !important;
|
height: 14px !important;
|
||||||
background-position: -223px -97px !important;
|
background-position: -223px -97px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'] .fullname-badged:before {
|
||||||
/* fix cut off badge in follow chirps */
|
/* fix cut off badge in follow chirps and detail view */
|
||||||
margin-top: -7px !important;
|
margin-top: -1px !important;
|
||||||
|
min-width: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-td-font='smallest'] .tweet-detail-wrapper .fullname-badged:before {
|
||||||
|
/* fix misaligned badge in detail view */
|
||||||
|
margin-top: -2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-td-font='small'] .tweet-detail-wrapper .fullname-badged:before {
|
||||||
|
/* fix misaligned badge in detail view */
|
||||||
|
margin-top: 0px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard-shortcut-list {
|
.keyboard-shortcut-list {
|
||||||
@@ -361,7 +400,7 @@ a:not(.tweet-detail-action) .reply-triangle {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-message.is-actionable span:hover > .icon-small-valigned {
|
.column-message.is-actionable > span:hover .icon {
|
||||||
/* add a visual response when hovering individual filter icons; black theme uses a value of 20 */
|
/* add a visual response when hovering individual filter icons; black theme uses a value of 20 */
|
||||||
filter: saturate(10);
|
filter: saturate(10);
|
||||||
}
|
}
|
||||||
@@ -386,6 +425,11 @@ a:not(.tweet-detail-action) .reply-triangle {
|
|||||||
border-width: 0 !important;
|
border-width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#actions-modal .mdl {
|
||||||
|
/* fix action dialog (RT, Add to List/Collection) width with small window size */
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
/* Fix glaring visual issues that twitter hasn't fixed yet smh */
|
||||||
/***************************************************************/
|
/***************************************************************/
|
||||||
@@ -427,18 +471,18 @@ a:not(.tweet-detail-action) .reply-triangle {
|
|||||||
/* Fix cut off usernames in Messages column */
|
/* Fix cut off usernames in Messages column */
|
||||||
/********************************************/
|
/********************************************/
|
||||||
|
|
||||||
[data-td-icon="icon-message"].is-shifted-1 .column-title-container {
|
.column[data-td-icon="icon-message"].is-shifted-1 .column-title-container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-bottom-color: transparent;
|
border-bottom-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tduck [data-td-icon="icon-message"].is-shifted-1 .column-title-items {
|
#tduck .column[data-td-icon="icon-message"].is-shifted-1 .column-title-items {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin-left: 4px !important;
|
margin-left: 4px !important;
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-td-icon="icon-message"].is-shifted-1 .username {
|
.column[data-td-icon="icon-message"].is-shifted-1 .username {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,7 @@ html, body {
|
|||||||
height: auto !important;
|
height: auto !important;
|
||||||
overflow-x: hidden !important;
|
overflow-x: hidden !important;
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::before {
|
body::before {
|
||||||
@@ -36,7 +37,7 @@ body::before {
|
|||||||
/* Square-ify stuff */
|
/* Square-ify stuff */
|
||||||
/********************/
|
/********************/
|
||||||
|
|
||||||
.media-item, .media-preview {
|
.media-item, .media-preview, .media-badge {
|
||||||
border-radius: 1px !important;
|
border-radius: 1px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +123,18 @@ html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'
|
|||||||
margin-left: -34px;
|
margin-left: -34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.td-notification-padded-alt .item-img {
|
||||||
|
/* td-notification-padded-alt is used alongside td-notification-padded to save space in the file */
|
||||||
|
position: relative;
|
||||||
|
left: 10px;
|
||||||
|
top: 2px;
|
||||||
|
width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.td-notification-padded-alt .activity-header > .nbfc > .avatar {
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/*********/
|
/*********/
|
||||||
/* Media */
|
/* Media */
|
||||||
/*********/
|
/*********/
|
||||||
@@ -159,6 +172,10 @@ html[data-td-font='smallest'] .fullname-badged:before, html[data-td-font='small'
|
|||||||
transition: opacity 0.15s ease;
|
transition: opacity 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.td-example #td-skip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.td-hover #td-skip {
|
.td-hover #td-skip {
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,17 @@
|
|||||||
margin: 0 auto !important;
|
margin: 0 auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ResponsiveLayout {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Section {
|
||||||
|
padding: 36px !important;
|
||||||
|
}
|
||||||
|
|
||||||
/*******************/
|
/*******************/
|
||||||
/* General styling */
|
/* General styling */
|
||||||
/*******************/
|
/*******************/
|
||||||
@@ -39,7 +50,7 @@ body {
|
|||||||
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
|
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar, .TopNav {
|
||||||
/* hide top bar */
|
/* hide top bar */
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@@ -65,3 +76,42 @@ button[type='submit'] {
|
|||||||
margin-top: 15px !important;
|
margin-top: 15px !important;
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************/
|
||||||
|
/* Fix min width and margins on logout page */
|
||||||
|
/********************************************/
|
||||||
|
|
||||||
|
html[logout] .page-canvas {
|
||||||
|
width: auto !important;
|
||||||
|
max-width: 888px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[logout] .signout-wrapper {
|
||||||
|
width: auto !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[logout] .signout {
|
||||||
|
margin: 60px 0 54px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[logout] .buttons {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************/
|
||||||
|
/* General logout page styling */
|
||||||
|
/*******************************/
|
||||||
|
|
||||||
|
html[logout] .aside {
|
||||||
|
/* hide elements around dialog */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[logout] .buttons button, html[logout] .buttons a {
|
||||||
|
/* style buttons */
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 4px !important;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.3) !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
@@ -8,15 +8,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let style = document.createElement("style");
|
let link = document.createElement("link");
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.href = "https://abs.twimg.com/tduck/css";
|
||||||
|
|
||||||
style.innerText = `#import "styles/twitter.base.css"`;
|
document.head.appendChild(link);
|
||||||
|
|
||||||
if (location.pathname === "/logout"){
|
if (location.pathname === "/logout"){
|
||||||
style.innerText += `#import "styles/twitter.logout.css"`;
|
document.documentElement.setAttribute("logout", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
document.head.appendChild(style);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(injectCSS, 1);
|
setTimeout(injectCSS, 1);
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
<?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.67.0.0-CI2662\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props')" />
|
<Import Project="packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props')" />
|
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" />
|
||||||
<Import Project="packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props')" />
|
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" />
|
||||||
<Import Project="packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props')" />
|
<Import Project="packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" />
|
||||||
|
<Import Project="packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.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>
|
||||||
@@ -45,6 +46,12 @@
|
|||||||
<LangVersion>7</LangVersion>
|
<LangVersion>7</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.WindowsAPICodePack, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\WindowsAPICodePack-Core.1.1.1\lib\Microsoft.WindowsAPICodePack.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.WindowsAPICodePack.Shell, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>packages\WindowsAPICodePack-Shell.1.1.1\lib\Microsoft.WindowsAPICodePack.Shell.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
@@ -57,6 +64,7 @@
|
|||||||
<Compile Include="Configuration\Instance\FileConfigInstance.cs" />
|
<Compile Include="Configuration\Instance\FileConfigInstance.cs" />
|
||||||
<Compile Include="Configuration\ConfigManager.cs" />
|
<Compile Include="Configuration\ConfigManager.cs" />
|
||||||
<Compile Include="Configuration\Instance\IConfigInstance.cs" />
|
<Compile Include="Configuration\Instance\IConfigInstance.cs" />
|
||||||
|
<Compile Include="Configuration\Instance\PluginConfigInstance.cs" />
|
||||||
<Compile Include="Configuration\LockManager.cs" />
|
<Compile Include="Configuration\LockManager.cs" />
|
||||||
<Compile Include="Configuration\SystemConfig.cs" />
|
<Compile Include="Configuration\SystemConfig.cs" />
|
||||||
<Compile Include="Configuration\UserConfig.cs" />
|
<Compile Include="Configuration\UserConfig.cs" />
|
||||||
@@ -90,12 +98,13 @@
|
|||||||
<DependentUpon>FormBrowser.cs</DependentUpon>
|
<DependentUpon>FormBrowser.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Core\Handling\General\FileDialogHandler.cs" />
|
<Compile Include="Core\Handling\General\FileDialogHandler.cs" />
|
||||||
|
<Compile Include="Core\Handling\KeyboardHandlerBase.cs" />
|
||||||
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\KeyboardHandlerBrowser.cs" />
|
||||||
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
<Compile Include="Core\Handling\KeyboardHandlerNotification.cs" />
|
||||||
<Compile Include="Core\Handling\RequestHandlerBase.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBase.cs" />
|
||||||
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
|
||||||
|
<Compile Include="Core\Handling\ResourceHandlerFactory.cs" />
|
||||||
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
|
||||||
<Compile Include="Core\Other\Interfaces\ITweetDeckBrowser.cs" />
|
|
||||||
<Compile Include="Core\Management\ContextInfo.cs" />
|
<Compile Include="Core\Management\ContextInfo.cs" />
|
||||||
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
|
<Compile Include="Core\Notification\Example\FormNotificationExample.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
@@ -260,10 +269,11 @@
|
|||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Plugins\Enums\PluginFolder.cs" />
|
<Compile Include="Plugins\Enums\PluginFolder.cs" />
|
||||||
|
<Compile Include="Plugins\IPluginConfig.cs" />
|
||||||
<Compile Include="Plugins\Plugin.cs" />
|
<Compile Include="Plugins\Plugin.cs" />
|
||||||
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
|
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
|
||||||
<Compile Include="Plugins\PluginBridge.cs" />
|
<Compile Include="Plugins\PluginBridge.cs" />
|
||||||
<Compile Include="Plugins\PluginConfig.cs" />
|
<Compile Include="Configuration\PluginConfig.cs" />
|
||||||
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
|
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
|
||||||
<Compile Include="Plugins\Enums\PluginGroup.cs" />
|
<Compile Include="Plugins\Enums\PluginGroup.cs" />
|
||||||
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
|
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
|
||||||
@@ -333,9 +343,9 @@
|
|||||||
<None Include="Resources\Images\icon-tray-new.ico" />
|
<None Include="Resources\Images\icon-tray-new.ico" />
|
||||||
<None Include="Resources\Images\icon-tray.ico" />
|
<None Include="Resources\Images\icon-tray.ico" />
|
||||||
<None Include="Resources\Images\spinner.apng" />
|
<None Include="Resources\Images\spinner.apng" />
|
||||||
<None Include="Resources\PostBuild.ps1" />
|
|
||||||
<None Include="Resources\Plugins\.debug\.meta" />
|
<None Include="Resources\Plugins\.debug\.meta" />
|
||||||
<None Include="Resources\Plugins\.debug\browser.js" />
|
<None Include="Resources\Plugins\.debug\browser.js" />
|
||||||
|
<None Include="Resources\Plugins\.debug\notification.js" />
|
||||||
<None Include="Resources\Plugins\clear-columns\.meta" />
|
<None Include="Resources\Plugins\clear-columns\.meta" />
|
||||||
<None Include="Resources\Plugins\clear-columns\browser.js" />
|
<None Include="Resources\Plugins\clear-columns\browser.js" />
|
||||||
<None Include="Resources\Plugins\edit-design\.meta" />
|
<None Include="Resources\Plugins\edit-design\.meta" />
|
||||||
@@ -354,7 +364,10 @@
|
|||||||
<None Include="Resources\Plugins\templates\modal.html" />
|
<None Include="Resources\Plugins\templates\modal.html" />
|
||||||
<None Include="Resources\Plugins\timeline-polls\.meta" />
|
<None Include="Resources\Plugins\timeline-polls\.meta" />
|
||||||
<None Include="Resources\Plugins\timeline-polls\browser.js" />
|
<None Include="Resources\Plugins\timeline-polls\browser.js" />
|
||||||
|
<None Include="Resources\PostBuild.fsx" />
|
||||||
|
<None Include="Resources\PostCefUpdate.ps1" />
|
||||||
<None Include="Resources\Scripts\code.js" />
|
<None Include="Resources\Scripts\code.js" />
|
||||||
|
<None Include="Resources\Scripts\imports\markup\introduction.html" />
|
||||||
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
|
<None Include="Resources\Scripts\imports\scripts\plugins.base.js" />
|
||||||
<None Include="Resources\Scripts\imports\styles\introduction.css" />
|
<None Include="Resources\Scripts\imports\styles\introduction.css" />
|
||||||
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
|
<None Include="Resources\Scripts\imports\styles\twitter.base.css" />
|
||||||
@@ -386,6 +399,15 @@
|
|||||||
<Name>TweetLib.Communication</Name>
|
<Name>TweetLib.Communication</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\Images\overlay-muted.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\Images\overlay-muted.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Resources\Images\overlay-notification.ico" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>rmdir "$(ProjectDir)bin\Debug"
|
<PostBuildEvent>rmdir "$(ProjectDir)bin\Debug"
|
||||||
@@ -394,15 +416,22 @@ rmdir "$(ProjectDir)bin\Release"
|
|||||||
rmdir "$(TargetDir)scripts" /S /Q
|
rmdir "$(TargetDir)scripts" /S /Q
|
||||||
rmdir "$(TargetDir)plugins" /S /Q
|
rmdir "$(TargetDir)plugins" /S /Q
|
||||||
|
|
||||||
powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild.ps1" "$(TargetDir)\" "$(ProjectDir)\" "$(ConfigurationName)"
|
IF EXIST "$(ProjectDir)bld\post_build.exe" (
|
||||||
|
"$(ProjectDir)bld\post_build.exe" "$(TargetDir)\" "$(ProjectDir)\" "$(ConfigurationName)"
|
||||||
|
) ELSE (
|
||||||
|
"$(DevEnvDir)CommonExtensions\Microsoft\FSharp\fsi.exe" "$(ProjectDir)Resources\PostBuild.fsx" --exec --nologo -- "$(TargetDir)\" "$(ProjectDir)\" "$(ConfigurationName)"
|
||||||
|
)
|
||||||
</PostBuildEvent>
|
</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<Target Name="BeforeBuild" Condition="(!$([System.IO.File]::Exists("$(ProjectDir)\bld\post_build.exe")) OR ($([System.IO.File]::GetLastWriteTime("$(ProjectDir)\Resources\PostBuild.fsx").Ticks) > $([System.IO.File]::GetLastWriteTime("$(ProjectDir)\bld\post_build.exe").Ticks)))">
|
||||||
|
<Exec Command=""$(ProjectDir)bld\POST BUILD.bat"" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" />
|
||||||
|
</Target>
|
||||||
<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">
|
<Target Name="AfterBuild" Condition="$(ConfigurationName) == Release">
|
||||||
<Exec Command="del "$(TargetDir)*.pdb"" />
|
<Exec Command="del "$(TargetDir)*.pdb"" />
|
||||||
<Exec Command="del "$(TargetDir)*.xml"" />
|
<Exec Command="del "$(TargetDir)*.xml"" />
|
||||||
<Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" />
|
<Delete Files="$(TargetDir)CefSharp.BrowserSubprocess.exe" />
|
||||||
<Delete Files="$(TargetDir)widevinecdmadapter.dll" />
|
<Delete Files="$(TargetDir)widevinecdmadapter.dll" />
|
||||||
<Exec Command=""$(ProjectDir)bld\UPDATE ONLY.bat"" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" />
|
<Exec Command="start "" /B "ISCC.exe" /Q "$(ProjectDir)bld\gen_upd.iss"" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" />
|
||||||
</Target>
|
</Target>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent>
|
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent>
|
||||||
@@ -411,11 +440,14 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use 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. Use 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.x64.3.3396.1777\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3396.1777\build\cef.redist.x64.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3396.1786\build\cef.redist.x64.props'))" />
|
||||||
<Error Condition="!Exists('packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3396.1777\build\cef.redist.x86.props'))" />
|
<Error Condition="!Exists('packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3396.1786\build\cef.redist.x86.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.props'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets'))" />
|
<Error Condition="!Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets'))" />
|
||||||
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0-CI2662\build\CefSharp.WinForms.props'))" />
|
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.props'))" />
|
||||||
|
<Error Condition="!Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets'))" />
|
||||||
|
<Error Condition="!Exists('packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
</Target>
|
</Target>
|
||||||
<Import Project="packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0-CI2662\build\CefSharp.Common.targets')" />
|
<Import Project="packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.67.0.0\build\CefSharp.Common.targets')" />
|
||||||
|
<Import Project="packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.67.0.0\build\CefSharp.WinForms.targets')" />
|
||||||
</Project>
|
</Project>
|
@@ -39,7 +39,7 @@ namespace TweetDuck.Updates{
|
|||||||
|
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
|
|
||||||
if (Program.UserConfig.EnableUpdateCheck){
|
if (Program.Config.User.EnableUpdateCheck){
|
||||||
DateTime now = DateTime.Now;
|
DateTime now = DateTime.Now;
|
||||||
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
TimeSpan nextHour = now.AddSeconds(60*(60-now.Minute)-now.Second)-now;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int Check(bool force){
|
public int Check(bool force){
|
||||||
if (Program.UserConfig.EnableUpdateCheck || force){
|
if (Program.Config.User.EnableUpdateCheck || force){
|
||||||
int nextEventId = unchecked(++lastEventId);
|
int nextEventId = unchecked(++lastEventId);
|
||||||
Task<UpdateInfo> checkTask = client.Check();
|
Task<UpdateInfo> checkTask = client.Check();
|
||||||
|
|
||||||
|
@@ -26,7 +26,7 @@ namespace TweetDuck.Updates{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void BeginSilentDownload(){
|
public void BeginSilentDownload(){
|
||||||
if (File.Exists(InstallerPath)){
|
if (WindowsUtils.FileExistsAndNotEmpty(InstallerPath)){
|
||||||
DownloadStatus = UpdateDownloadStatus.Done;
|
DownloadStatus = UpdateDownloadStatus.Done;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ namespace TweetDuck.Updates{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentDownload = BrowserUtils.DownloadFileAsync(downloadUrl, InstallerPath, () => {
|
currentDownload = BrowserUtils.DownloadFileAsync(downloadUrl, InstallerPath, null, () => {
|
||||||
DownloadStatus = UpdateDownloadStatus.Done;
|
DownloadStatus = UpdateDownloadStatus.Done;
|
||||||
currentDownload = null;
|
currentDownload = null;
|
||||||
}, e => {
|
}, e => {
|
||||||
|
16
bld/POST BUILD.bat
Normal file
16
bld/POST BUILD.bat
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
DEL "post_build.exe"
|
||||||
|
|
||||||
|
SET fsc="%PROGRAMFILES(x86)%\Microsoft SDKs\F#\10.1\Framework\v4.0\fsc.exe"
|
||||||
|
|
||||||
|
IF NOT EXIST %fsc% (
|
||||||
|
SET fsc="%PROGRAMFILES%\Microsoft SDKs\F#\10.1\Framework\v4.0\fsc.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
IF NOT EXIST %fsc% (
|
||||||
|
ECHO fsc.exe not found
|
||||||
|
EXIT 1
|
||||||
|
)
|
||||||
|
|
||||||
|
%fsc% --standalone --deterministic --preferreduilang:en-US --platform:x86 --target:exe --out:post_build.exe "%~dp0..\Resources\PostBuild.fsx"
|
@@ -1 +0,0 @@
|
|||||||
start "" /B "ISCC.exe" /Q "gen_upd.iss"
|
|
@@ -220,10 +220,19 @@ begin
|
|||||||
end;
|
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. }
|
||||||
|
{ TODO: Remove workaround that forces full installation for 1.16 and older eventually. }
|
||||||
function TDIsMatchingCEFVersion: Boolean;
|
function TDIsMatchingCEFVersion: Boolean;
|
||||||
var CEFVersion: String;
|
var CEFVersion: String;
|
||||||
|
var TDVersionMS: Cardinal;
|
||||||
|
var TDVersionLS: Cardinal;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
if (GetVersionNumbers(UpdatePath+'TweetDuck.exe', TDVersionMS, TDVersionLS)) and ((TDVersionMS and $FFFF) < 17) then
|
||||||
|
begin
|
||||||
|
Result := False
|
||||||
|
Exit
|
||||||
|
end;
|
||||||
|
|
||||||
Result := (GetVersionNumbersString(UpdatePath+'libcef.dll', CEFVersion) and (CompareStr(CEFVersion, '{#CefVersion}') = 0))
|
Result := (GetVersionNumbersString(UpdatePath+'libcef.dll', CEFVersion) and (CompareStr(CEFVersion, '{#CefVersion}') = 0))
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.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>
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
<AssemblyName>TweetLib.Communication</AssemblyName>
|
<AssemblyName>TweetLib.Communication</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@@ -40,5 +43,14 @@
|
|||||||
<Compile Include="DuplexPipe.cs" />
|
<Compile Include="DuplexPipe.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
4
lib/TweetLib.Communication/packages.config
Normal file
4
lib/TweetLib.Communication/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
|
||||||
|
</packages>
|
@@ -1,5 +1,6 @@
|
|||||||
<?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\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
@@ -17,6 +18,8 @@
|
|||||||
<IsCodedUITest>False</IsCodedUITest>
|
<IsCodedUITest>False</IsCodedUITest>
|
||||||
<TestProjectType>UnitTest</TestProjectType>
|
<TestProjectType>UnitTest</TestProjectType>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
@@ -55,6 +58,9 @@
|
|||||||
<Name>TweetDuck</Name>
|
<Name>TweetDuck</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Choose>
|
<Choose>
|
||||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -75,6 +81,12 @@
|
|||||||
</Choose>
|
</Choose>
|
||||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
|
</Target>
|
||||||
<!-- 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">
|
||||||
|
4
lib/TweetTest.System/packages.config
Normal file
4
lib/TweetTest.System/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
|
||||||
|
</packages>
|
@@ -1,7 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="cef.redist.x64" version="3.3396.1777" targetFramework="net452" />
|
<package id="cef.redist.x64" version="3.3396.1786" targetFramework="net452" />
|
||||||
<package id="cef.redist.x86" version="3.3396.1777" targetFramework="net452" />
|
<package id="cef.redist.x86" version="3.3396.1786" targetFramework="net452" />
|
||||||
<package id="CefSharp.Common" version="67.0.0-CI2662" targetFramework="net452" />
|
<package id="CefSharp.Common" version="67.0.0" targetFramework="net452" />
|
||||||
<package id="CefSharp.WinForms" version="67.0.0-CI2662" targetFramework="net452" />
|
<package id="CefSharp.WinForms" version="67.0.0" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
|
||||||
|
<package id="WindowsAPICodePack-Core" version="1.1.1" targetFramework="net452" />
|
||||||
|
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net452" />
|
||||||
</packages>
|
</packages>
|
@@ -1,17 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using CefSharp.BrowserSubprocess;
|
using CefSharp.BrowserSubprocess;
|
||||||
|
|
||||||
namespace TweetDuck.Browser{
|
namespace TweetDuck.Browser{
|
||||||
static class Program{
|
static class Program{
|
||||||
internal const string Version = "1.4.1.0";
|
internal const string Version = "1.4.2";
|
||||||
|
|
||||||
private static int Main(string[] args){
|
private static int Main(string[] args){
|
||||||
SubProcess.EnableHighDPISupport();
|
SubProcess.EnableHighDPISupport();
|
||||||
|
|
||||||
const string typePrefix = "--type=";
|
string FindArg(string key){
|
||||||
string type = Array.Find(args, arg => arg.StartsWith(typePrefix, StringComparison.OrdinalIgnoreCase)).Substring(typePrefix.Length);
|
return Array.Find(args, arg => arg.StartsWith(key, StringComparison.OrdinalIgnoreCase)).Substring(key.Length);
|
||||||
|
}
|
||||||
|
|
||||||
if (type == "renderer"){
|
const string typePrefix = "--type=";
|
||||||
|
const string parentIdPrefix = "--host-process-id=";
|
||||||
|
|
||||||
|
if (!int.TryParse(FindArg(parentIdPrefix), out int parentId)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => KillWhenHung(parentId), TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
|
if (FindArg(typePrefix) == "renderer"){
|
||||||
using(SubProcess subProcess = new SubProcess(args)){
|
using(SubProcess subProcess = new SubProcess(args)){
|
||||||
return subProcess.Run();
|
return subProcess.Run();
|
||||||
}
|
}
|
||||||
@@ -20,5 +32,18 @@ namespace TweetDuck.Browser{
|
|||||||
return SubProcess.ExecuteProcess();
|
return SubProcess.ExecuteProcess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async void KillWhenHung(int parentId){
|
||||||
|
try{
|
||||||
|
using(Process process = Process.GetProcessById(parentId)){
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
}catch{
|
||||||
|
// ded
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(10000);
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.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>
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
@@ -28,7 +31,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=67.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=67.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\CefSharp.Common.67.0.0-CI2662\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
<HintPath>..\packages\CefSharp.Common.67.0.0\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -36,9 +39,18 @@
|
|||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
<PostBuildEvent>call "$(DevEnvDir)..\..\VC\Auxiliary\Build\vcvars32.bat"
|
||||||
editbin /largeaddressaware /TSAWARE "$(TargetPath)"</PostBuildEvent>
|
editbin /largeaddressaware /TSAWARE "$(TargetPath)"</PostBuildEvent>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.9.0\build\Microsoft.Net.Compilers.props'))" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
4
subprocess/packages.config
Normal file
4
subprocess/packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Microsoft.Net.Compilers" version="2.9.0" targetFramework="net452" developmentDependency="true" />
|
||||||
|
</packages>
|
@@ -19,7 +19,14 @@ namespace TweetDuck.Video.Controls{
|
|||||||
Form form = control.FindForm();
|
Form form = control.FindForm();
|
||||||
System.Diagnostics.Debug.Assert(form != null);
|
System.Diagnostics.Debug.Assert(form != null);
|
||||||
|
|
||||||
Text = tooltipFunc(args);
|
string text = tooltipFunc(args);
|
||||||
|
|
||||||
|
if (text == null){
|
||||||
|
Visible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text = text;
|
||||||
|
|
||||||
Point loc = form.PointToClient(control.Parent.PointToScreen(new Point(control.Location.X+(followCursor ? args.X : control.Width/2), 0)));
|
Point loc = form.PointToClient(control.Parent.PointToScreen(new Point(control.Location.X+(followCursor ? args.X : control.Width/2), 0)));
|
||||||
loc.X = Math.Max(0, Math.Min(form.Width-Width, loc.X-Width/2));
|
loc.X = Math.Max(0, Math.Min(form.Width-Width, loc.X-Width/2));
|
||||||
|
@@ -30,7 +30,7 @@ namespace TweetDuck.Video.Controls{
|
|||||||
brushBack.Color = Parent.BackColor;
|
brushBack.Color = Parent.BackColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle rect = e.ClipRectangle;
|
Rectangle rect = new Rectangle(0, 0, Width, Height);
|
||||||
Point cursor = PointToClient(Cursor.Position);
|
Point cursor = PointToClient(Cursor.Position);
|
||||||
int width = rect.Width-1;
|
int width = rect.Width-1;
|
||||||
int progress = (int)(width*((double)Value/Maximum));
|
int progress = (int)(width*((double)Value/Maximum));
|
||||||
|
155
video/FormPlayer.Designer.cs
generated
155
video/FormPlayer.Designer.cs
generated
@@ -26,16 +26,18 @@
|
|||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
this.timerSync = new System.Windows.Forms.Timer(this.components);
|
this.timerSync = new System.Windows.Forms.Timer(this.components);
|
||||||
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
this.trackBarVolume = new System.Windows.Forms.TrackBar();
|
||||||
this.tablePanel = new System.Windows.Forms.TableLayoutPanel();
|
this.tablePanelFull = new System.Windows.Forms.TableLayoutPanel();
|
||||||
this.progressSeek = new TweetDuck.Video.Controls.SeekBar();
|
this.progressSeek = new TweetDuck.Video.Controls.SeekBar();
|
||||||
this.labelTime = new System.Windows.Forms.Label();
|
this.labelTime = new System.Windows.Forms.Label();
|
||||||
this.timerData = new System.Windows.Forms.Timer(this.components);
|
|
||||||
this.labelTooltip = new TweetDuck.Video.Controls.LabelTooltip();
|
|
||||||
this.imageResize = new System.Windows.Forms.PictureBox();
|
this.imageResize = new System.Windows.Forms.PictureBox();
|
||||||
this.imageDownload = new System.Windows.Forms.PictureBox();
|
this.imageDownload = new System.Windows.Forms.PictureBox();
|
||||||
this.imageClose = new System.Windows.Forms.PictureBox();
|
this.imageClose = new System.Windows.Forms.PictureBox();
|
||||||
|
this.timerData = new System.Windows.Forms.Timer(this.components);
|
||||||
|
this.tablePanelCompactBottom = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.tablePanelCompactTop = new System.Windows.Forms.TableLayoutPanel();
|
||||||
|
this.labelTooltip = new TweetDuck.Video.Controls.LabelTooltip();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
|
||||||
this.tablePanel.SuspendLayout();
|
this.tablePanelFull.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageResize)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageResize)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageClose)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageClose)).BeginInit();
|
||||||
@@ -64,30 +66,32 @@
|
|||||||
this.trackBarVolume.MouseDown += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseDown);
|
this.trackBarVolume.MouseDown += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseDown);
|
||||||
this.trackBarVolume.MouseUp += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseUp);
|
this.trackBarVolume.MouseUp += new System.Windows.Forms.MouseEventHandler(this.trackBarVolume_MouseUp);
|
||||||
//
|
//
|
||||||
// tablePanel
|
// tablePanelFull
|
||||||
//
|
//
|
||||||
this.tablePanel.BackColor = System.Drawing.SystemColors.Control;
|
this.tablePanelFull.BackColor = System.Drawing.SystemColors.Control;
|
||||||
this.tablePanel.ColumnCount = 6;
|
this.tablePanelFull.ColumnCount = 6;
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 74F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
this.tablePanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
this.tablePanelFull.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
this.tablePanel.Controls.Add(this.trackBarVolume, 3, 0);
|
this.tablePanelFull.Controls.Add(this.trackBarVolume, 3, 0);
|
||||||
this.tablePanel.Controls.Add(this.progressSeek, 1, 0);
|
this.tablePanelFull.Controls.Add(this.progressSeek, 1, 0);
|
||||||
this.tablePanel.Controls.Add(this.labelTime, 2, 0);
|
this.tablePanelFull.Controls.Add(this.labelTime, 2, 0);
|
||||||
this.tablePanel.Controls.Add(this.imageResize, 5, 0);
|
this.tablePanelFull.Controls.Add(this.imageResize, 5, 0);
|
||||||
this.tablePanel.Controls.Add(this.imageDownload, 4, 0);
|
this.tablePanelFull.Controls.Add(this.imageDownload, 4, 0);
|
||||||
this.tablePanel.Controls.Add(this.imageClose, 0, 0);
|
this.tablePanelFull.Controls.Add(this.imageClose, 0, 0);
|
||||||
this.tablePanel.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.tablePanelFull.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
this.tablePanel.Location = new System.Drawing.Point(0, 86);
|
this.tablePanelFull.Enabled = false;
|
||||||
this.tablePanel.Name = "tablePanel";
|
this.tablePanelFull.Location = new System.Drawing.Point(0, 86);
|
||||||
this.tablePanel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
this.tablePanelFull.Name = "tablePanelFull";
|
||||||
this.tablePanel.RowCount = 1;
|
this.tablePanelFull.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
this.tablePanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
this.tablePanelFull.RowCount = 1;
|
||||||
this.tablePanel.Size = new System.Drawing.Size(400, 34);
|
this.tablePanelFull.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
this.tablePanel.TabIndex = 1;
|
this.tablePanelFull.Size = new System.Drawing.Size(400, 34);
|
||||||
|
this.tablePanelFull.TabIndex = 0;
|
||||||
|
this.tablePanelFull.Visible = false;
|
||||||
//
|
//
|
||||||
// progressSeek
|
// progressSeek
|
||||||
//
|
//
|
||||||
@@ -98,7 +102,7 @@
|
|||||||
this.progressSeek.Margin = new System.Windows.Forms.Padding(9, 10, 8, 10);
|
this.progressSeek.Margin = new System.Windows.Forms.Padding(9, 10, 8, 10);
|
||||||
this.progressSeek.Maximum = 5000;
|
this.progressSeek.Maximum = 5000;
|
||||||
this.progressSeek.Name = "progressSeek";
|
this.progressSeek.Name = "progressSeek";
|
||||||
this.progressSeek.Size = new System.Drawing.Size(91, 14);
|
this.progressSeek.Size = new System.Drawing.Size(85, 14);
|
||||||
this.progressSeek.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
|
this.progressSeek.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
|
||||||
this.progressSeek.TabIndex = 0;
|
this.progressSeek.TabIndex = 0;
|
||||||
this.progressSeek.MouseDown += new System.Windows.Forms.MouseEventHandler(this.progressSeek_MouseDown);
|
this.progressSeek.MouseDown += new System.Windows.Forms.MouseEventHandler(this.progressSeek_MouseDown);
|
||||||
@@ -106,32 +110,14 @@
|
|||||||
// labelTime
|
// labelTime
|
||||||
//
|
//
|
||||||
this.labelTime.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.labelTime.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.labelTime.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular);
|
this.labelTime.Font = new System.Drawing.Font("Segoe UI", 9F);
|
||||||
this.labelTime.Location = new System.Drawing.Point(138, 3);
|
this.labelTime.Location = new System.Drawing.Point(132, 3);
|
||||||
this.labelTime.Margin = new System.Windows.Forms.Padding(0, 3, 0, 5);
|
this.labelTime.Margin = new System.Windows.Forms.Padding(0, 3, 0, 5);
|
||||||
this.labelTime.Name = "labelTime";
|
this.labelTime.Name = "labelTime";
|
||||||
this.labelTime.Size = new System.Drawing.Size(74, 26);
|
this.labelTime.Size = new System.Drawing.Size(80, 26);
|
||||||
this.labelTime.TabIndex = 1;
|
this.labelTime.TabIndex = 1;
|
||||||
this.labelTime.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
this.labelTime.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||||
//
|
//
|
||||||
// timerData
|
|
||||||
//
|
|
||||||
this.timerData.Interval = 500;
|
|
||||||
this.timerData.Tick += new System.EventHandler(this.timerData_Tick);
|
|
||||||
//
|
|
||||||
// labelTooltip
|
|
||||||
//
|
|
||||||
this.labelTooltip.AutoSize = true;
|
|
||||||
this.labelTooltip.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
|
||||||
this.labelTooltip.ForeColor = System.Drawing.Color.White;
|
|
||||||
this.labelTooltip.Location = new System.Drawing.Point(0, 0);
|
|
||||||
this.labelTooltip.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
|
||||||
this.labelTooltip.Name = "labelTooltip";
|
|
||||||
this.labelTooltip.Padding = new System.Windows.Forms.Padding(4, 2, 2, 2);
|
|
||||||
this.labelTooltip.Size = new System.Drawing.Size(6, 19);
|
|
||||||
this.labelTooltip.TabIndex = 2;
|
|
||||||
this.labelTooltip.Visible = false;
|
|
||||||
//
|
|
||||||
// imageResize
|
// imageResize
|
||||||
//
|
//
|
||||||
this.imageResize.Cursor = System.Windows.Forms.Cursors.Hand;
|
this.imageResize.Cursor = System.Windows.Forms.Cursors.Hand;
|
||||||
@@ -178,6 +164,66 @@
|
|||||||
this.imageClose.WaitOnLoad = true;
|
this.imageClose.WaitOnLoad = true;
|
||||||
this.imageClose.Click += new System.EventHandler(this.imageClose_Click);
|
this.imageClose.Click += new System.EventHandler(this.imageClose_Click);
|
||||||
//
|
//
|
||||||
|
// timerData
|
||||||
|
//
|
||||||
|
this.timerData.Interval = 500;
|
||||||
|
this.timerData.Tick += new System.EventHandler(this.timerData_Tick);
|
||||||
|
//
|
||||||
|
// tablePanelCompactBottom
|
||||||
|
//
|
||||||
|
this.tablePanelCompactBottom.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.tablePanelCompactBottom.ColumnCount = 5;
|
||||||
|
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
|
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
|
||||||
|
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
|
this.tablePanelCompactBottom.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 28F));
|
||||||
|
this.tablePanelCompactBottom.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
|
this.tablePanelCompactBottom.Enabled = false;
|
||||||
|
this.tablePanelCompactBottom.Location = new System.Drawing.Point(0, 52);
|
||||||
|
this.tablePanelCompactBottom.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
|
||||||
|
this.tablePanelCompactBottom.Name = "tablePanelCompactBottom";
|
||||||
|
this.tablePanelCompactBottom.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
|
||||||
|
this.tablePanelCompactBottom.RowCount = 1;
|
||||||
|
this.tablePanelCompactBottom.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tablePanelCompactBottom.Size = new System.Drawing.Size(400, 34);
|
||||||
|
this.tablePanelCompactBottom.TabIndex = 1;
|
||||||
|
//
|
||||||
|
// tablePanelCompactTop
|
||||||
|
//
|
||||||
|
this.tablePanelCompactTop.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||||
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
|
this.tablePanelCompactTop.BackColor = System.Drawing.SystemColors.Control;
|
||||||
|
this.tablePanelCompactTop.ColumnCount = 2;
|
||||||
|
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F));
|
||||||
|
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||||
|
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||||
|
this.tablePanelCompactTop.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||||
|
this.tablePanelCompactTop.Enabled = false;
|
||||||
|
this.tablePanelCompactTop.Location = new System.Drawing.Point(0, 60);
|
||||||
|
this.tablePanelCompactTop.Margin = new System.Windows.Forms.Padding(0);
|
||||||
|
this.tablePanelCompactTop.Name = "tablePanelCompactTop";
|
||||||
|
this.tablePanelCompactTop.Padding = new System.Windows.Forms.Padding(2, 0, 4, 0);
|
||||||
|
this.tablePanelCompactTop.RowCount = 1;
|
||||||
|
this.tablePanelCompactTop.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||||
|
this.tablePanelCompactTop.Size = new System.Drawing.Size(400, 34);
|
||||||
|
this.tablePanelCompactTop.TabIndex = 2;
|
||||||
|
this.tablePanelCompactTop.Visible = false;
|
||||||
|
//
|
||||||
|
// labelTooltip
|
||||||
|
//
|
||||||
|
this.labelTooltip.AutoSize = true;
|
||||||
|
this.labelTooltip.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
|
||||||
|
this.labelTooltip.ForeColor = System.Drawing.Color.White;
|
||||||
|
this.labelTooltip.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.labelTooltip.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0);
|
||||||
|
this.labelTooltip.Name = "labelTooltip";
|
||||||
|
this.labelTooltip.Padding = new System.Windows.Forms.Padding(4, 2, 2, 2);
|
||||||
|
this.labelTooltip.Size = new System.Drawing.Size(6, 19);
|
||||||
|
this.labelTooltip.TabIndex = 3;
|
||||||
|
this.labelTooltip.Visible = false;
|
||||||
|
//
|
||||||
// FormPlayer
|
// FormPlayer
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
@@ -186,12 +232,13 @@
|
|||||||
this.ClientSize = new System.Drawing.Size(400, 120);
|
this.ClientSize = new System.Drawing.Size(400, 120);
|
||||||
this.ControlBox = false;
|
this.ControlBox = false;
|
||||||
this.Controls.Add(this.labelTooltip);
|
this.Controls.Add(this.labelTooltip);
|
||||||
this.Controls.Add(this.tablePanel);
|
this.Controls.Add(this.tablePanelCompactBottom);
|
||||||
|
this.Controls.Add(this.tablePanelFull);
|
||||||
|
this.Controls.Add(this.tablePanelCompactTop);
|
||||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||||
this.Location = new System.Drawing.Point(-32000, -32000);
|
this.Location = new System.Drawing.Point(-32000, -32000);
|
||||||
this.MaximizeBox = false;
|
this.MaximizeBox = false;
|
||||||
this.MinimizeBox = false;
|
this.MinimizeBox = false;
|
||||||
this.MinimumSize = new System.Drawing.Size(400, 120);
|
|
||||||
this.Name = "FormPlayer";
|
this.Name = "FormPlayer";
|
||||||
this.ShowIcon = false;
|
this.ShowIcon = false;
|
||||||
this.ShowInTaskbar = false;
|
this.ShowInTaskbar = false;
|
||||||
@@ -199,7 +246,7 @@
|
|||||||
this.Text = "TweetDuck Video";
|
this.Text = "TweetDuck Video";
|
||||||
this.Load += new System.EventHandler(this.FormPlayer_Load);
|
this.Load += new System.EventHandler(this.FormPlayer_Load);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
|
||||||
this.tablePanel.ResumeLayout(false);
|
this.tablePanelFull.ResumeLayout(false);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageResize)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageResize)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageDownload)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.imageClose)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.imageClose)).EndInit();
|
||||||
@@ -212,7 +259,7 @@
|
|||||||
|
|
||||||
private System.Windows.Forms.Timer timerSync;
|
private System.Windows.Forms.Timer timerSync;
|
||||||
private System.Windows.Forms.TrackBar trackBarVolume;
|
private System.Windows.Forms.TrackBar trackBarVolume;
|
||||||
private System.Windows.Forms.TableLayoutPanel tablePanel;
|
private System.Windows.Forms.TableLayoutPanel tablePanelFull;
|
||||||
private Controls.SeekBar progressSeek;
|
private Controls.SeekBar progressSeek;
|
||||||
private System.Windows.Forms.Label labelTime;
|
private System.Windows.Forms.Label labelTime;
|
||||||
private System.Windows.Forms.Timer timerData;
|
private System.Windows.Forms.Timer timerData;
|
||||||
@@ -220,6 +267,8 @@
|
|||||||
private System.Windows.Forms.PictureBox imageResize;
|
private System.Windows.Forms.PictureBox imageResize;
|
||||||
private System.Windows.Forms.PictureBox imageDownload;
|
private System.Windows.Forms.PictureBox imageDownload;
|
||||||
private System.Windows.Forms.PictureBox imageClose;
|
private System.Windows.Forms.PictureBox imageClose;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tablePanelCompactBottom;
|
||||||
|
private System.Windows.Forms.TableLayoutPanel tablePanelCompactTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,9 +10,17 @@ using WMPLib;
|
|||||||
|
|
||||||
namespace TweetDuck.Video{
|
namespace TweetDuck.Video{
|
||||||
sealed partial class FormPlayer : Form{
|
sealed partial class FormPlayer : Form{
|
||||||
|
private bool IsCursorOverVideo{
|
||||||
|
get{
|
||||||
|
Point cursor = PointToClient(Cursor.Position);
|
||||||
|
return cursor.Y < (tablePanelFull.Enabled ? tablePanelFull.Location.Y : tablePanelCompactTop.Location.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool ShowWithoutActivation => true;
|
protected override bool ShowWithoutActivation => true;
|
||||||
|
|
||||||
private readonly IntPtr ownerHandle;
|
private readonly IntPtr ownerHandle;
|
||||||
|
private readonly float ownerDpi;
|
||||||
private readonly string videoUrl;
|
private readonly string videoUrl;
|
||||||
private readonly DuplexPipe pipe;
|
private readonly DuplexPipe pipe;
|
||||||
|
|
||||||
@@ -23,14 +31,21 @@ namespace TweetDuck.Video{
|
|||||||
|
|
||||||
private WindowsMediaPlayer Player => player.Ocx;
|
private WindowsMediaPlayer Player => player.Ocx;
|
||||||
|
|
||||||
public FormPlayer(IntPtr handle, int volume, string url, string token){
|
public FormPlayer(IntPtr handle, int dpi, int volume, string url, string token){
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
this.ownerHandle = handle;
|
this.ownerHandle = handle;
|
||||||
|
this.ownerDpi = dpi / 100F;
|
||||||
this.videoUrl = url;
|
this.videoUrl = url;
|
||||||
this.pipe = DuplexPipe.CreateClient(token);
|
this.pipe = DuplexPipe.CreateClient(token);
|
||||||
this.pipe.DataIn += pipe_DataIn;
|
this.pipe.DataIn += pipe_DataIn;
|
||||||
|
|
||||||
|
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
|
||||||
|
ClientSize = new Size(0, 0);
|
||||||
|
Location = new Point((rect.Left+rect.Right)/2, (rect.Top+rect.Bottom)/2);
|
||||||
|
Opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
player = new ControlWMP{
|
player = new ControlWMP{
|
||||||
Dock = DockStyle.Fill
|
Dock = DockStyle.Fill
|
||||||
};
|
};
|
||||||
@@ -51,6 +66,10 @@ namespace TweetDuck.Video{
|
|||||||
trackBarVolume.Value = volume; // changes player volume too if non-default
|
trackBarVolume.Value = volume; // changes player volume too if non-default
|
||||||
|
|
||||||
labelTooltip.AttachTooltip(progressSeek, true, args => {
|
labelTooltip.AttachTooltip(progressSeek, true, args => {
|
||||||
|
if (args.X < 0 || args.Y < 0 || args.X >= progressSeek.Width || args.Y >= progressSeek.Height){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
IWMPMedia media = Player.currentMedia;
|
IWMPMedia media = Player.currentMedia;
|
||||||
int progress = (int)(media.duration*progressSeek.GetProgress(args.X));
|
int progress = (int)(media.duration*progressSeek.GetProgress(args.X));
|
||||||
|
|
||||||
@@ -68,6 +87,59 @@ namespace TweetDuck.Video{
|
|||||||
Application.AddMessageFilter(new MessageFilter(this));
|
Application.AddMessageFilter(new MessageFilter(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Layout
|
||||||
|
|
||||||
|
private int DpiScaled(int value){
|
||||||
|
return (int)Math.Round(value*ownerDpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshControlPanel(){
|
||||||
|
bool useCompactLayout = ClientSize.Width < DpiScaled(480);
|
||||||
|
bool needsUpdate = !timerSync.Enabled || (useCompactLayout ? tablePanelFull.Enabled : tablePanelCompactBottom.Enabled);
|
||||||
|
|
||||||
|
if (needsUpdate){
|
||||||
|
void Disable(TableLayoutPanel panel){
|
||||||
|
panel.Controls.Clear();
|
||||||
|
panel.Visible = false;
|
||||||
|
panel.Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tablePanelFull.SuspendLayout();
|
||||||
|
tablePanelCompactBottom.SuspendLayout();
|
||||||
|
tablePanelCompactTop.SuspendLayout();
|
||||||
|
|
||||||
|
if (useCompactLayout){
|
||||||
|
Disable(tablePanelFull);
|
||||||
|
|
||||||
|
tablePanelCompactBottom.Enabled = true;
|
||||||
|
tablePanelCompactBottom.Controls.Add(imageClose, 0, 0);
|
||||||
|
tablePanelCompactBottom.Controls.Add(trackBarVolume, 2, 0);
|
||||||
|
tablePanelCompactBottom.Controls.Add(imageDownload, 3, 0);
|
||||||
|
tablePanelCompactBottom.Controls.Add(imageResize, 4, 0);
|
||||||
|
|
||||||
|
tablePanelCompactTop.Enabled = true;
|
||||||
|
tablePanelCompactTop.Controls.Add(progressSeek, 0, 0);
|
||||||
|
tablePanelCompactTop.Controls.Add(labelTime, 1, 0);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Disable(tablePanelCompactBottom);
|
||||||
|
Disable(tablePanelCompactTop);
|
||||||
|
|
||||||
|
tablePanelFull.Enabled = true;
|
||||||
|
tablePanelFull.Controls.Add(imageClose, 0, 0);
|
||||||
|
tablePanelFull.Controls.Add(progressSeek, 1, 0);
|
||||||
|
tablePanelFull.Controls.Add(labelTime, 2, 0);
|
||||||
|
tablePanelFull.Controls.Add(trackBarVolume, 3, 0);
|
||||||
|
tablePanelFull.Controls.Add(imageDownload, 4, 0);
|
||||||
|
tablePanelFull.Controls.Add(imageResize, 5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
tablePanelFull.ResumeLayout();
|
||||||
|
tablePanelCompactBottom.ResumeLayout();
|
||||||
|
tablePanelCompactTop.ResumeLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
|
|
||||||
private void FormPlayer_Load(object sender, EventArgs e){
|
private void FormPlayer_Load(object sender, EventArgs e){
|
||||||
@@ -97,9 +169,14 @@ namespace TweetDuck.Video{
|
|||||||
else if (state == WMPPlayState.wmppsPlaying){
|
else if (state == WMPPlayState.wmppsPlaying){
|
||||||
Player.PlayStateChange -= player_PlayStateChange;
|
Player.PlayStateChange -= player_PlayStateChange;
|
||||||
|
|
||||||
timerSync.Start();
|
|
||||||
NativeMethods.SetWindowOwner(Handle, ownerHandle);
|
NativeMethods.SetWindowOwner(Handle, ownerHandle);
|
||||||
Cursor.Current = Cursors.Default;
|
Cursor.Current = Cursors.Default;
|
||||||
|
|
||||||
|
SuspendLayout();
|
||||||
|
timerSync_Tick(timerSync, EventArgs.Empty);
|
||||||
|
timerSync.Start();
|
||||||
|
Opacity = 1;
|
||||||
|
ResumeLayout(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,20 +192,38 @@ namespace TweetDuck.Video{
|
|||||||
[HandleProcessCorruptedStateExceptions]
|
[HandleProcessCorruptedStateExceptions]
|
||||||
private void timerSync_Tick(object sender, EventArgs e){
|
private void timerSync_Tick(object sender, EventArgs e){
|
||||||
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
|
if (NativeMethods.GetWindowRect(ownerHandle, out NativeMethods.RECT rect)){
|
||||||
int width = rect.Right-rect.Left+1;
|
|
||||||
int height = rect.Bottom-rect.Top+1;
|
|
||||||
|
|
||||||
IWMPMedia media = Player.currentMedia;
|
IWMPMedia media = Player.currentMedia;
|
||||||
IWMPControls controls = Player.controls;
|
IWMPControls controls = Player.controls;
|
||||||
|
|
||||||
|
int ownerLeft = rect.Left;
|
||||||
|
int ownerTop = rect.Top;
|
||||||
|
int ownerWidth = rect.Right-rect.Left+1;
|
||||||
|
int ownerHeight = rect.Bottom-rect.Top+1;
|
||||||
|
|
||||||
|
// roughly matches MinimumSize for client bounds, adjusted a bit for weirdness with higher DPI
|
||||||
|
int minWidth = DpiScaled(356);
|
||||||
|
int minHeight = DpiScaled(386);
|
||||||
|
|
||||||
|
if (NativeMethods.GetClientRect(ownerHandle, out NativeMethods.RECT clientSize)){
|
||||||
|
minWidth = Math.Min(minWidth, clientSize.Right);
|
||||||
|
minHeight = Math.Min(minHeight, clientSize.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxWidth = Math.Min(DpiScaled(media.imageSourceWidth), ownerWidth*3/4);
|
||||||
|
int maxHeight = Math.Min(DpiScaled(media.imageSourceHeight), ownerHeight*3/4);
|
||||||
|
|
||||||
bool isCursorInside = ClientRectangle.Contains(PointToClient(Cursor.Position));
|
bool isCursorInside = ClientRectangle.Contains(PointToClient(Cursor.Position));
|
||||||
|
|
||||||
ClientSize = new Size(Math.Max(MinimumSize.Width, Math.Min(media.imageSourceWidth, width*3/4)), Math.Max(MinimumSize.Height, Math.Min(media.imageSourceHeight, height*3/4)));
|
Size newSize = new Size(Math.Max(minWidth+2, maxWidth), Math.Max(minHeight+2, maxHeight));
|
||||||
Location = new Point(rect.Left+(width-ClientSize.Width)/2, rect.Top+(height-ClientSize.Height+SystemInformation.CaptionHeight)/2);
|
Point newLocation = new Point(ownerLeft+(ownerWidth-newSize.Width)/2, ownerTop+(ownerHeight-newSize.Height+SystemInformation.CaptionHeight)/2);
|
||||||
|
|
||||||
tablePanel.Visible = isCursorInside || isDragging;
|
if (ClientSize != newSize || Location != newLocation){
|
||||||
|
ClientSize = newSize;
|
||||||
|
Location = newLocation;
|
||||||
|
RefreshControlPanel();
|
||||||
|
}
|
||||||
|
|
||||||
if (tablePanel.Visible){
|
if (isCursorInside || isDragging){
|
||||||
labelTime.Text = $"{controls.currentPositionString} / {media.durationString}";
|
labelTime.Text = $"{controls.currentPositionString} / {media.durationString}";
|
||||||
|
|
||||||
int value = (int)Math.Round(progressSeek.Maximum*controls.currentPosition/media.duration);
|
int value = (int)Math.Round(progressSeek.Maximum*controls.currentPosition/media.duration);
|
||||||
@@ -142,6 +237,19 @@ namespace TweetDuck.Video{
|
|||||||
progressSeek.Value = value+1;
|
progressSeek.Value = value+1;
|
||||||
progressSeek.Value = value;
|
progressSeek.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tablePanelFull.Enabled){
|
||||||
|
tablePanelFull.Visible = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tablePanelCompactBottom.Visible = true;
|
||||||
|
tablePanelCompactTop.Visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tablePanelFull.Visible = false;
|
||||||
|
tablePanelCompactBottom.Visible = false;
|
||||||
|
tablePanelCompactTop.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controls.currentPosition > media.duration){ // pausing near the end of the video causes WMP to play beyond the end of the video wtf
|
if (controls.currentPosition > media.duration){ // pausing near the end of the video causes WMP to play beyond the end of the video wtf
|
||||||
@@ -157,6 +265,10 @@ namespace TweetDuck.Video{
|
|||||||
|
|
||||||
if (isCursorInside && !wasCursorInside){
|
if (isCursorInside && !wasCursorInside){
|
||||||
wasCursorInside = true;
|
wasCursorInside = true;
|
||||||
|
|
||||||
|
if (IsCursorOverVideo){
|
||||||
|
Cursor.Current = Cursors.Default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!isCursorInside && wasCursorInside){
|
else if (!isCursorInside && wasCursorInside){
|
||||||
wasCursorInside = false;
|
wasCursorInside = false;
|
||||||
@@ -273,26 +385,19 @@ namespace TweetDuck.Video{
|
|||||||
internal sealed class MessageFilter : IMessageFilter{
|
internal sealed class MessageFilter : IMessageFilter{
|
||||||
private readonly FormPlayer form;
|
private readonly FormPlayer form;
|
||||||
|
|
||||||
private bool IsCursorOverVideo{
|
|
||||||
get{
|
|
||||||
Point cursor = form.PointToClient(Cursor.Position);
|
|
||||||
return cursor.Y < form.tablePanel.Location.Y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageFilter(FormPlayer form){
|
public MessageFilter(FormPlayer form){
|
||||||
this.form = form;
|
this.form = form;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IMessageFilter.PreFilterMessage(ref Message m){
|
bool IMessageFilter.PreFilterMessage(ref Message m){
|
||||||
if (m.Msg == 0x0201){ // WM_LBUTTONDOWN
|
if (m.Msg == 0x0201){ // WM_LBUTTONDOWN
|
||||||
if (IsCursorOverVideo){
|
if (form.IsCursorOverVideo){
|
||||||
form.TogglePause();
|
form.TogglePause();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m.Msg == 0x0203){ // WM_LBUTTONDBLCLK
|
else if (m.Msg == 0x0203){ // WM_LBUTTONDBLCLK
|
||||||
if (IsCursorOverVideo){
|
if (form.IsCursorOverVideo){
|
||||||
form.TogglePause();
|
form.TogglePause();
|
||||||
form.Player.fullScreen = !form.Player.fullScreen;
|
form.Player.fullScreen = !form.Player.fullScreen;
|
||||||
return true;
|
return true;
|
||||||
|
@@ -9,6 +9,10 @@ namespace TweetDuck.Video{
|
|||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user