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

Compare commits

..

61 Commits

Author SHA1 Message Date
980bf2c307 Release 1.18.3 2020-02-16 18:04:21 +01:00
762aea1e20 Add option to set first day of week in date picker
Closes #276
2020-02-16 15:47:26 +01:00
c1aefc7163 Add a way to register callbacks when $TDX property object gets updated 2020-02-16 14:39:15 +01:00
9480ba26e0 Move system tray options to a separate tab & reorganize General tab 2020-02-16 14:39:15 +01:00
c2c9160ed9 Allow dragging Twitter account links onto the app to view their profile
Closes #288
2020-02-16 11:35:20 +01:00
175b067a17 Add missing tooltip on custom video player selection 2020-02-16 11:27:46 +01:00
9d8656ca20 Add option for whether dev tools window should stay on top 2020-02-16 11:15:25 +01:00
0863001c80 Add option for custom video player executable & arguments 2020-02-15 21:17:44 +01:00
0ee22a30ad Add option for custom browser args w/ new dialog & disallow quotes in URLs 2020-02-15 20:26:07 +01:00
447697ba45 Add StringUtils.NullIfEmpty & update existing code to use it 2020-02-15 17:25:00 +01:00
aea77ff909 Make dev tools dialog a proper window that appears in taskbar 2020-02-15 15:18:16 +01:00
af5da76f72 Make video player open tweet URL instead of video URL if playback fails 2020-02-15 14:34:19 +01:00
a369c65451 Release 1.18.2 2019-10-23 02:26:53 +02:00
318f65f187 Merge branch 'master' of https://github.com/chylex/TweetDuck 2019-10-17 13:04:19 +02:00
1cd60e831c Add a few missing translation languages 2019-10-12 18:30:12 +02:00
b988959eaa Only activate mouse hook while cursor is over the notification window 2019-10-07 04:49:39 +02:00
1eb1f9848a Prepare login/logout page scripts and styles for Twitter redesign & minor fixes 2019-10-07 03:01:17 +02:00
7f6cc0da01 Fix mouse back/forward button triggering navigation if history wasn't empty
Closes #286
2019-10-06 14:47:49 +02:00
19fcb69525 Fix prebuild event not killing hung browser processes reliably 2019-09-05 01:27:36 +02:00
22cef0a44c Fix C# version in secondary projects 2019-09-05 00:48:18 +02:00
2459d31bff Remove RegexOptions.Compiled where not needed 2019-09-05 00:16:25 +02:00
19f104239a Fix missing spaces in C#/F# code 2019-08-23 01:56:31 +02:00
bd0be65038 Minor refactoring & removal of unnecessary code 2019-08-23 01:56:18 +02:00
bbb7907e54 Move LockManager to TweetLib.Core & remove WindowsUtils.CurrentProcessID 2019-08-22 06:29:32 +02:00
a6963a18d4 Move debug resource hot swap into a separate class 2019-08-21 10:31:30 +02:00
92716ea3e0 Move URL-related code from UrlUtils & TwitterUtils to TwitterUrls 2019-08-21 10:12:19 +02:00
aec4c1feea Move TweetNotification to TweetLib.Core as DesktopNotification 2019-08-21 10:12:19 +02:00
d505b3305b Initial refactoring of ScriptLoader & making it accessible in TweetLib.Core 2019-08-21 10:12:19 +02:00
a34a02e14d Generalize PluginListFlowLayout and move it 2019-07-15 00:49:28 +02:00
26d2d7a51e Move PluginManager to Core lib & refactor plugin enums 2019-07-14 20:44:25 +02:00
c2f7e52d13 Add IAppSystemHandler w/ OpenFileExplorer and update existing code to use it 2019-07-14 20:44:25 +02:00
de68d8934d Add IScriptExecutor w/ implementation for CefSharp browser 2019-07-14 17:15:14 +02:00
4fdf7fc958 Release 1.18.1 2019-07-13 19:50:49 +02:00
42a5e72f19 Revert README change & lock Inno Setup to version 5.6.1 2019-07-13 19:39:08 +02:00
f7359ebc8a Update README with instructions for fixing Inno Download Plugin 2019-07-13 18:44:02 +02:00
f395ac53dc Fix wrong colors in dropdown menus w/ black theme 2019-07-13 18:22:38 +02:00
1113e0b559 Fix new image url parser not checking if an extension already exists 2019-07-13 18:16:11 +02:00
5e3bd31862 Delete corrupted downloads after an error 2019-07-13 18:10:16 +02:00
11d978dad1 Fix GIF thumbnails not loading after Twitter changed image urls
Closes #271
2019-07-13 17:51:16 +02:00
f7961024d7 Enable popup for linking another account
Closes #269
2019-07-13 06:17:30 +02:00
72973a8707 Restore smooth scrolling in columns
Fixes #251
2019-07-13 06:07:01 +02:00
68254f48d5 Fix TweetDeck bug with broken DM image previews
References #271
2019-07-13 00:48:53 +02:00
eac4f30c50 Support new image urls & fix missing filename features w/o Best Image Quality
Fixes #270
2019-07-13 00:40:27 +02:00
25680fa980 Add StringUtils.SplitInTwo & use it in RequestHandlerBase 2019-07-12 22:17:29 +02:00
ff5e1da14d Fix wrong 'X columns on screen' width calculation after a TweetDeck update 2019-06-03 11:30:14 +02:00
95afff7879 Update F# compiler location 2019-06-03 10:33:57 +02:00
50bd526025 Continue refactoring and moving plugin code 2019-05-27 19:46:39 +02:00
108a0fefc3 Fix PluginManager crashing after error(s) during plugin execution 2019-05-27 19:38:53 +02:00
dd8c5d27be Update code to use C# 8 switch expression 2019-05-27 16:04:08 +02:00
b2937bc776 Fix broken image upload dialog in new composer 2019-05-27 12:37:30 +02:00
4d8e764211 Release 1.18 2019-05-26 21:29:46 +02:00
544b8664fd Add edit-design plugin option to set composer/drawer size 2019-05-26 18:41:23 +02:00
d0610865bd Fix wrong background color in screenshots 2019-05-26 18:12:40 +02:00
ebc0b51590 Merge branch 'master' of https://github.com/chylex/TweetDuck 2019-05-26 18:03:17 +02:00
4487f1169e Fix composer input refocus & emoji keyboard broken after switching composers 2019-05-26 18:02:11 +02:00
85559b6083 Fix and refactor 'Stay open' pin, that was broken after composer update 2019-05-26 18:01:47 +02:00
1056273c57 Add a custom JS event when the old composer is activated 2019-05-26 17:58:58 +02:00
61af2ebc8b Fix template panel not hiding when switching to different drawer/new composer 2019-05-26 17:57:23 +02:00
9121c86656 Update README (drop VS 2017, update support section) 2019-05-26 15:03:16 +02:00
1ccefe853a Update .NET & begin refactoring code into a core lib (#264)
* Switch to .NET Framework 4.7.2 & C# 8.0, update libraries

* Add TweetLib.Core project targeting .NET Standard 2.0

* Enable reference nullability checks for TweetLib.Core

* Move a bunch of utility classes into TweetLib.Core & refactor

* Partially move TweetDuck plugin & update system to TweetLib.Core

* Move some constants and CultureInfo setup to TweetLib.Core

* Move some configuration classes to TweetLib.Core

* Minor refactoring and warning suppression

* Add App to TweetLib.Core

* Add IAppErrorHandler w/ implementation

* Continue moving config, plugin, and update classes to TweetLib.Core

* Fix a few nullability checks

* Update installers to check for .NET Framework 4.7.2
2019-05-26 14:55:12 +02:00
aca438b837 Create FUNDING.yml 2019-05-25 10:17:51 +02:00
194 changed files with 3842 additions and 2715 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
# These are supported funding model platforms
patreon: chylex
ko_fi: chylex

View File

@@ -1,5 +1,5 @@
using System; using System;
using TweetDuck.Data; using TweetLib.Core.Collections;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
static class Arguments{ static class Arguments{
@@ -22,8 +22,8 @@ namespace TweetDuck.Configuration{
return Current.HasFlag(flag); return Current.HasFlag(flag);
} }
public static string GetValue(string key, string defaultValue){ public static string GetValue(string key){
return Current.GetValue(key, defaultValue); return Current.GetValue(key);
} }
public static CommandLineArgs GetCurrentClean(){ public static CommandLineArgs GetCurrentClean(){

View File

@@ -1,13 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using TweetDuck.Configuration.Instance;
using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Data.Serialization; using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Serialization.Converters;
using TweetLib.Core.Utils;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class ConfigManager{ sealed class ConfigManager : IConfigManager{
public UserConfig User { get; } public UserConfig User { get; }
public SystemConfig System { get; } public SystemConfig System { get; }
public PluginConfig Plugins { get; } public PluginConfig Plugins { get; }
@@ -16,7 +16,7 @@ namespace TweetDuck.Configuration{
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 PluginConfigInstance<PluginConfig> infoPlugins;
private readonly IConfigInstance<BaseConfig>[] infoList; private readonly IConfigInstance<BaseConfig>[] infoList;
@@ -28,7 +28,7 @@ namespace TweetDuck.Configuration{
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) infoPlugins = new PluginConfigInstance<PluginConfig>(Program.PluginConfigFilePath, Plugins)
}; };
// TODO refactor further // TODO refactor further
@@ -70,59 +70,13 @@ namespace TweetDuck.Configuration{
infoPlugins.Reload(); infoPlugins.Reload();
} }
private void TriggerProgramRestartRequested(){ void IConfigManager.TriggerProgramRestartRequested(){
ProgramRestartRequested?.Invoke(this, EventArgs.Empty); ProgramRestartRequested?.Invoke(this, EventArgs.Empty);
} }
private IConfigInstance<BaseConfig> GetInstanceInfo(BaseConfig instance){ IConfigInstance<BaseConfig> IConfigManager.GetInstanceInfo(BaseConfig instance){
Type instanceType = instance.GetType(); Type instanceType = instance.GetType();
return Array.Find(infoList, info => info.Instance.GetType() == instanceType); // TODO handle null return Array.Find(infoList, info => info.Instance.GetType() == instanceType); // TODO handle null
} }
public abstract class BaseConfig{
private readonly ConfigManager configManager;
protected BaseConfig(ConfigManager configManager){
this.configManager = configManager;
}
// Management
public void Save(){
configManager.GetInstanceInfo(this).Save();
}
public void Reload(){
configManager.GetInstanceInfo(this).Reload();
}
public void Reset(){
configManager.GetInstanceInfo(this).Reset();
}
// Construction methods
public T ConstructWithDefaults<T>() where T : BaseConfig{
return ConstructWithDefaults(configManager) as T;
}
protected abstract BaseConfig ConstructWithDefaults(ConfigManager configManager);
// Utility methods
protected void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){
if (!EqualityComparer<T>.Default.Equals(field, value)){
field = value;
eventHandler?.Invoke(this, EventArgs.Empty);
}
}
protected void UpdatePropertyWithRestartRequest<T>(ref T field, T value){
if (!EqualityComparer<T>.Default.Equals(field, value)){
field = value;
configManager.TriggerProgramRestartRequested();
}
}
}
} }
} }

View File

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

View File

@@ -1,20 +1,41 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using TweetDuck.Plugins; using TweetLib.Core.Features.Configuration;
using TweetDuck.Plugins.Events; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class PluginConfig : ConfigManager.BaseConfig, IPluginConfig{ sealed class PluginConfig : BaseConfig, IPluginConfig{
private static readonly string[] DefaultDisabled = { private static readonly string[] DefaultDisabled = {
"official/clear-columns", "official/clear-columns",
"official/reply-account" "official/reply-account"
}; };
// CONFIGURATION // CONFIGURATION DATA
public IEnumerable<string> DisabledPlugins => disabled; private readonly HashSet<string> disabled = new HashSet<string>(DefaultDisabled);
// EVENTS
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState; public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
// END OF CONFIG
public PluginConfig(IConfigManager configManager) : base(configManager){}
protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
return new PluginConfig(configManager);
}
// INTERFACE IMPLEMENTATION
IEnumerable<string> IPluginConfig.DisabledPlugins => disabled;
void IPluginConfig.Reset(IEnumerable<string> newDisabledPlugins){
disabled.Clear();
disabled.UnionWith(newDisabledPlugins);
}
public void SetEnabled(Plugin plugin, bool enabled){ public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){ if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
@@ -26,20 +47,5 @@ namespace TweetDuck.Configuration{
public bool IsEnabled(Plugin plugin){ public bool IsEnabled(Plugin plugin){
return !disabled.Contains(plugin.Identifier); 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);
}
} }
} }

View File

@@ -1,5 +1,7 @@
namespace TweetDuck.Configuration{ using TweetLib.Core.Features.Configuration;
sealed class SystemConfig : ConfigManager.BaseConfig{
namespace TweetDuck.Configuration{
sealed class SystemConfig : BaseConfig{
// CONFIGURATION DATA // CONFIGURATION DATA
@@ -17,9 +19,9 @@
// END OF CONFIG // END OF CONFIG
public SystemConfig(ConfigManager configManager) : base(configManager){} public SystemConfig(IConfigManager configManager) : base(configManager){}
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){ protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
return new SystemConfig(configManager); return new SystemConfig(configManager);
} }
} }

View File

@@ -1,13 +1,14 @@
using System; using System;
using System.Drawing; using System.Drawing;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class UserConfig : ConfigManager.BaseConfig{ sealed class UserConfig : BaseConfig{
// CONFIGURATION DATA // CONFIGURATION DATA
@@ -29,16 +30,20 @@ namespace TweetDuck.Configuration{
private string _customCefArgs = null; private string _customCefArgs = null;
public string BrowserPath { get; set; } = null; public string BrowserPath { get; set; } = null;
public string BrowserPathArgs { get; set; } = null;
public bool IgnoreTrackingUrlWarning { get; set; } = false; public bool IgnoreTrackingUrlWarning { get; set; } = false;
public string SearchEngineUrl { get; set; } = null; public string SearchEngineUrl { get; set; } = null;
private int _zoomLevel = 100; private int _zoomLevel = 100;
public int VideoPlayerVolume { get; set; } = 50; public string VideoPlayerPath { get; set; } = null;
public string VideoPlayerPathArgs { get; set; } = null;
public int VideoPlayerVolume { get; set; } = 50;
public bool EnableSpellCheck { get; set; } = false; public bool EnableSpellCheck { get; set; } = false;
private string _spellCheckLanguage = "en-US"; private string _spellCheckLanguage = "en-US";
public string TranslationTarget { get; set; } = "en"; public string TranslationTarget { get; set; } = "en";
public int CalendarFirstDay { get; set; } = -1;
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled; private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
public bool EnableTrayHighlight { get; set; } = true; public bool EnableTrayHighlight { get; set; } = true;
@@ -56,14 +61,14 @@ namespace TweetDuck.Configuration{
public bool NotificationTimerCountDown { get; set; } = false; public bool NotificationTimerCountDown { get; set; } = false;
public int NotificationDurationValue { get; set; } = 25; public int NotificationDurationValue { get; set; } = 25;
public TweetNotification.Position NotificationPosition { get; set; } = TweetNotification.Position.TopRight; public DesktopNotification.Position NotificationPosition { get; set; } = DesktopNotification.Position.TopRight;
public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation; public Point CustomNotificationPosition { get; set; } = ControlExtensions.InvisibleLocation;
public int NotificationDisplay { get; set; } = 0; public int NotificationDisplay { get; set; } = 0;
public int NotificationEdgeDistance { get; set; } = 8; public int NotificationEdgeDistance { get; set; } = 8;
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto; public DesktopNotification.Size NotificationSize { get; set; } = DesktopNotification.Size.Auto;
public Size CustomNotificationSize { get; set; } = Size.Empty; public Size CustomNotificationSize { get; set; } = Size.Empty;
public int NotificationScrollSpeed { get; set; } = 100; public int NotificationScrollSpeed { get; set; } = 100;
private string _notificationSoundPath; private string _notificationSoundPath;
private int _notificationSoundVolume = 100; private int _notificationSoundVolume = 100;
@@ -72,6 +77,8 @@ namespace TweetDuck.Configuration{
public string CustomBrowserCSS { get; set; } = null; public string CustomBrowserCSS { get; set; } = null;
public string CustomNotificationCSS { get; set; } = null; public string CustomNotificationCSS { get; set; } = null;
public bool DevToolsWindowOnTop { get; set; } = true;
// SPECIAL PROPERTIES // SPECIAL PROPERTIES
@@ -79,7 +86,7 @@ namespace TweetDuck.Configuration{
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty; public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
public bool IsCustomSoundNotificationSet => NotificationSoundPath != string.Empty; public bool IsCustomSoundNotificationSet => NotificationSoundPath != string.Empty;
public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default; public ImageQuality TwitterImageQuality => BestImageQuality ? ImageQuality.Best : ImageQuality.Default;
public string NotificationSoundPath{ public string NotificationSoundPath{
get => _notificationSoundPath ?? string.Empty; get => _notificationSoundPath ?? string.Empty;
@@ -135,9 +142,9 @@ namespace TweetDuck.Configuration{
// END OF CONFIG // END OF CONFIG
public UserConfig(ConfigManager configManager) : base(configManager){} public UserConfig(IConfigManager configManager) : base(configManager){}
protected override ConfigManager.BaseConfig ConstructWithDefaults(ConfigManager configManager){ protected override BaseConfig ConstructWithDefaults(IConfigManager configManager){
return new UserConfig(configManager); return new UserConfig(configManager);
} }
} }

View File

@@ -0,0 +1,41 @@
using System.IO;
using CefSharp;
using TweetLib.Core.Browser;
namespace TweetDuck.Core.Adapters{
sealed class CefScriptExecutor : IScriptExecutor{
private readonly IWebBrowser browser;
public CefScriptExecutor(IWebBrowser browser){
this.browser = browser;
}
public void RunFunction(string name, params object[] args){
browser.ExecuteScriptAsync(name, args);
}
public void RunScript(string identifier, string script){
using IFrame frame = browser.GetMainFrame();
RunScript(frame, script, identifier);
}
public bool RunFile(string file){
using IFrame frame = browser.GetMainFrame();
return RunFile(frame, file);
}
// Helpers
public static void RunScript(IFrame frame, string script, string identifier){
if (script != null){
frame.ExecuteJavaScriptAsync(script, identifier, 1);
}
}
public static bool RunFile(IFrame frame, string file){
string script = Program.Resources.Load(file);
RunScript(frame, script, "root:" + Path.GetFileNameWithoutExtension(file));
return script != null;
}
}
}

View File

@@ -1,5 +1,7 @@
using System.Text; using System.Text;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetLib.Core;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
static class PropertyBridge{ static class PropertyBridge{
@@ -8,11 +10,11 @@ namespace TweetDuck.Core.Bridge{
} }
public static string GenerateScript(Environment environment){ public static string GenerateScript(Environment environment){
string Bool(bool value) => value ? "true;" : "false;"; static string Bool(bool value) => value ? "true;" : "false;";
string Str(string value) => '"'+value+"\";"; static string Str(string value) => $"\"{value}\";";
UserConfig config = Program.Config.User; UserConfig config = Program.Config.User;
StringBuilder build = new StringBuilder(128).Append("(function(x){"); StringBuilder build = new StringBuilder(384).Append("(function(x){");
build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover)); build.Append("x.expandLinksOnHover=").Append(Bool(config.ExpandLinksOnHover));
@@ -23,13 +25,14 @@ namespace TweetDuck.Core.Bridge{
build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications)); build.Append("x.muteNotifications=").Append(Bool(config.MuteNotifications));
build.Append("x.notificationMediaPreviews=").Append(Bool(config.NotificationMediaPreviews)); build.Append("x.notificationMediaPreviews=").Append(Bool(config.NotificationMediaPreviews));
build.Append("x.translationTarget=").Append(Str(config.TranslationTarget)); build.Append("x.translationTarget=").Append(Str(config.TranslationTarget));
build.Append("x.firstDayOfWeek=").Append(config.CalendarFirstDay == -1 ? LocaleUtils.GetJQueryDayOfWeek(Lib.Culture.DateTimeFormat.FirstDayOfWeek) : config.CalendarFirstDay);
} }
if (environment == Environment.Notification){ if (environment == Environment.Notification){
build.Append("x.skipOnLinkClick=").Append(Bool(config.NotificationSkipOnLinkClick)); build.Append("x.skipOnLinkClick=").Append(Bool(config.NotificationSkipOnLinkClick));
} }
return build.Append("})(window.$TDX=window.$TDX||{})").ToString(); return build.Append("})(window.$TDX=window.$TDX||{});if(window.TDGF_onPropertiesUpdated)window.TDGF_onPropertiesUpdated()").ToString();
} }
} }
} }

View File

@@ -1,18 +1,19 @@
using System.Windows.Forms; using System.Diagnostics.CodeAnalysis;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Management; using TweetDuck.Core.Handling;
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 TweetLib.Core.Features.Notifications;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
class TweetDeckBridge{ class TweetDeckBridge{
public static string FontSize { get; private set; }
public static string NotificationHeadLayout { get; private set; }
public static readonly ContextInfo ContextInfo = new ContextInfo();
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
FontSize = NotificationHeadLayout = null; FormNotificationBase.FontSize = null;
FormNotificationBase.HeadLayout = null;
} }
private readonly FormBrowser form; private readonly FormBrowser form;
@@ -44,17 +45,17 @@ namespace TweetDuck.Core.Bridge{
public void LoadNotificationLayout(string fontSize, string headLayout){ public void LoadNotificationLayout(string fontSize, string headLayout){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
FontSize = fontSize; FormNotificationBase.FontSize = fontSize;
NotificationHeadLayout = headLayout; FormNotificationBase.HeadLayout = headLayout;
}); });
} }
public void SetRightClickedLink(string type, string url){ public void SetRightClickedLink(string type, string url){
ContextInfo.SetLink(type, url); ContextMenuBase.CurrentInfo.SetLink(type, url);
} }
public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){ public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages); ContextMenuBase.CurrentInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
} }
public void DisplayTooltip(string text){ public void DisplayTooltip(string text){
@@ -63,7 +64,7 @@ namespace TweetDuck.Core.Bridge{
} }
// Notification only // Notification only
public sealed class Notification : TweetDeckBridge{ public sealed class Notification : TweetDeckBridge{
public Notification(FormBrowser form, FormNotificationMain notification) : base(form, notification){} public Notification(FormBrowser form, FormNotificationMain notification) : base(form, notification){}
@@ -85,7 +86,7 @@ namespace TweetDuck.Core.Bridge{
public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){ public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
notification.InvokeAsyncSafe(() => { notification.InvokeAsyncSafe(() => {
form.OnTweetNotification(); form.OnTweetNotification();
notification.ShowNotification(new TweetNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl)); notification.ShowNotification(new DesktopNotification(columnId, chirpId, columnName, tweetHtml, tweetCharacters, tweetUrl, quoteUrl));
}); });
} }
@@ -100,8 +101,12 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width)); form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width));
} }
public void PlayVideo(string url, string username){ public void PlayVideo(string videoUrl, string tweetUrl, string username, IJavascriptCallback callShowOverlay){
form.InvokeAsyncSafe(() => form.PlayVideo(url, username)); form.InvokeAsyncSafe(() => form.PlayVideo(videoUrl, tweetUrl, username, callShowOverlay));
}
public void StopVideo(){
form.InvokeAsyncSafe(form.StopVideo);
} }
public void FixClipboard(){ public void FixClipboard(){
@@ -117,14 +122,12 @@ namespace TweetDuck.Core.Bridge{
} }
public void Alert(string type, string contents){ public void Alert(string type, string contents){
MessageBoxIcon icon; MessageBoxIcon icon = type switch{
"error" => MessageBoxIcon.Error,
switch(type){ "warning" => MessageBoxIcon.Warning,
case "error": icon = MessageBoxIcon.Error; break; "info" => MessageBoxIcon.Information,
case "warning": icon = MessageBoxIcon.Warning; break; _ => MessageBoxIcon.None
case "info": icon = MessageBoxIcon.Information; break; };
default: icon = MessageBoxIcon.None; break;
}
FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK); FormMessage.Show("TweetDuck Browser Message", contents, icon, FormMessage.OK);
} }

View File

@@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Updates; using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
class UpdateBridge{ class UpdateBridge{
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private readonly Control sync; private readonly Control sync;
@@ -11,7 +13,6 @@ namespace TweetDuck.Core.Bridge{
private UpdateInfo nextUpdate = null; private UpdateInfo nextUpdate = null;
public event EventHandler<UpdateInfo> UpdateAccepted; public event EventHandler<UpdateInfo> UpdateAccepted;
public event EventHandler<UpdateInfo> UpdateDelayed;
public event EventHandler<UpdateInfo> UpdateDismissed; public event EventHandler<UpdateInfo> UpdateDismissed;
public UpdateBridge(UpdateHandler updates, Control sync){ public UpdateBridge(UpdateHandler updates, Control sync){
@@ -54,10 +55,6 @@ namespace TweetDuck.Core.Bridge{
HandleInteractionEvent(UpdateAccepted); HandleInteractionEvent(UpdateAccepted);
} }
public void OnUpdateDelayed(){
HandleInteractionEvent(UpdateDelayed);
}
public void OnUpdateDismissed(){ public void OnUpdateDismissed(){
HandleInteractionEvent(UpdateDismissed); HandleInteractionEvent(UpdateDismissed);

View File

@@ -21,9 +21,8 @@ namespace TweetDuck.Core.Controls{
} }
public static float GetDPIScale(this Control control){ public static float GetDPIScale(this Control control){
using(Graphics graphics = control.CreateGraphics()){ using Graphics graphics = control.CreateGraphics();
return graphics.DpiY/96F; return graphics.DpiY / 96F;
}
} }
public static bool IsFullyOutsideView(this Form form){ public static bool IsFullyOutsideView(this Form form){
@@ -31,17 +30,17 @@ namespace TweetDuck.Core.Controls{
} }
public static void MoveToCenter(this Form targetForm, Form parentForm){ public static void MoveToCenter(this Form targetForm, Form parentForm){
targetForm.Location = new Point(parentForm.Location.X+parentForm.Width/2-targetForm.Width/2, parentForm.Location.Y+parentForm.Height/2-targetForm.Height/2); targetForm.Location = new Point(parentForm.Location.X + (parentForm.Width / 2) - (targetForm.Width / 2), parentForm.Location.Y + (parentForm.Height / 2) - (targetForm.Height / 2));
} }
public static void SetValueInstant(this ProgressBar bar, int value){ public static void SetValueInstant(this ProgressBar bar, int value){
if (value == bar.Maximum){ if (value == bar.Maximum){
bar.Value = value; bar.Value = value;
bar.Value = value-1; bar.Value = value - 1;
bar.Value = value; bar.Value = value;
} }
else{ else{
bar.Value = value+1; bar.Value = value + 1;
bar.Value = value; bar.Value = value;
} }
} }
@@ -60,10 +59,11 @@ namespace TweetDuck.Core.Controls{
public static bool AlignValueToTick(this TrackBar trackBar){ public static bool AlignValueToTick(this TrackBar trackBar){
if (trackBar.Value % trackBar.SmallChange != 0){ if (trackBar.Value % trackBar.SmallChange != 0){
trackBar.Value = trackBar.SmallChange*(int)Math.Floor(((double)trackBar.Value/trackBar.SmallChange)+0.5); trackBar.Value = trackBar.SmallChange * (int)Math.Floor(((double)trackBar.Value / trackBar.SmallChange) + 0.5);
return false; return false;
} }
else return true;
return true;
} }
public static void EnableMultilineShortcuts(this TextBox textBox){ public static void EnableMultilineShortcuts(this TextBox textBox){

View File

@@ -23,7 +23,7 @@ namespace TweetDuck.Core.Controls{
} }
Rectangle rect = e.ClipRectangle; Rectangle rect = e.ClipRectangle;
rect.Width = (int)(rect.Width*((double)Value/Maximum)); rect.Width = (int)(rect.Width * ((double)Value / Maximum));
e.Graphics.FillRectangle(brush, rect); e.Graphics.FillRectangle(brush, rect);
} }

View File

@@ -1,13 +1,8 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Plugins.Controls{ namespace TweetDuck.Core.Controls{
sealed class PluginListFlowLayout : FlowLayoutPanel{ sealed class FlowLayoutPanelNoHScroll : FlowLayoutPanel{
public PluginListFlowLayout(){
FlowDirection = FlowDirection.TopDown;
WrapContents = false;
}
protected override void WndProc(ref Message m){ protected override void WndProc(ref Message m){
if (m.Msg == 0x85){ // WM_NCPAINT if (m.Msg == 0x85){ // WM_NCPAINT
NativeMethods.ShowScrollBar(Handle, NativeMethods.SB_HORZ, false); // basically fuck the horizontal scrollbar very much NativeMethods.ShowScrollBar(Handle, NativeMethods.SB_HORZ, false); // basically fuck the horizontal scrollbar very much

View File

@@ -7,16 +7,15 @@ namespace TweetDuck.Core.Controls{
public int LineHeight { get; set; } public int LineHeight { get; set; }
protected override void OnPaint(PaintEventArgs e){ protected override void OnPaint(PaintEventArgs e){
int y = (int)Math.Floor((ClientRectangle.Height-Text.Length*LineHeight)/2F)-1; int y = (int)Math.Floor((ClientRectangle.Height - Text.Length * LineHeight) / 2F) - 1;
using Brush brush = new SolidBrush(ForeColor);
using(Brush brush = new SolidBrush(ForeColor)){ foreach(char chr in Text){
foreach(char chr in Text){ string str = chr.ToString();
string str = chr.ToString(); float x = (ClientRectangle.Width - e.Graphics.MeasureString(str, Font).Width) / 2F;
float x = (ClientRectangle.Width-e.Graphics.MeasureString(str, Font).Width)/2F;
e.Graphics.DrawString(str, Font, brush, x, y); e.Graphics.DrawString(str, Font, brush, x, y);
y += LineHeight; y += LineHeight;
}
} }
} }
} }

View File

@@ -1,7 +1,10 @@
using System; using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Taskbar; using CefSharp;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
@@ -14,10 +17,10 @@ using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Analytics; 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.Events;
using TweetDuck.Resources;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Events;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{ sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
@@ -51,7 +54,6 @@ 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;
@@ -65,7 +67,7 @@ namespace TweetDuck.Core{
Text = Program.BrandName; Text = Program.BrandName;
this.plugins = new PluginManager(Program.Config.Plugins, Program.PluginPath); this.plugins = new PluginManager(Program.Config.Plugins, Program.PluginPath, Program.PluginDataPath);
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();
@@ -73,12 +75,11 @@ namespace TweetDuck.Core{
this.notification = new FormNotificationTweet(this, plugins); this.notification = new FormNotificationTweet(this, plugins);
this.notification.Show(); this.notification.Show();
this.updates = new UpdateHandler(Program.InstallerPath); this.updates = new UpdateHandler(new UpdateCheckClient(Program.InstallerPath), TaskScheduler.FromCurrentSynchronizationContext());
this.updates.CheckFinished += updates_CheckFinished; this.updates.CheckFinished += updates_CheckFinished;
this.updateBridge = new UpdateBridge(updates, this); this.updateBridge = new UpdateBridge(updates, this);
this.updateBridge.UpdateAccepted += updateBridge_UpdateAccepted; this.updateBridge.UpdateAccepted += updateBridge_UpdateAccepted;
this.updateBridge.UpdateDelayed += updateBridge_UpdateDelayed;
this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed; this.updateBridge.UpdateDismissed += updateBridge_UpdateDismissed;
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge.Browser(this, notification), updateBridge); this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge.Browser(this, notification), updateBridge);
@@ -86,9 +87,6 @@ namespace TweetDuck.Core{
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
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;
@@ -96,7 +94,6 @@ 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();
@@ -111,6 +108,10 @@ 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,6 +137,10 @@ 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();
} }
@@ -150,7 +155,6 @@ 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
@@ -212,6 +216,7 @@ 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();
} }
@@ -232,7 +237,9 @@ namespace TweetDuck.Core{
private void plugins_Reloaded(object sender, PluginErrorEventArgs e){ private void plugins_Reloaded(object sender, PluginErrorEventArgs e){
if (e.HasErrors){ if (e.HasErrors){
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); this.InvokeAsyncSafe(() => { // TODO not needed but makes code consistent...
FormMessage.Error("Error Loading Plugins", "The following plugins will not be available until the issues are resolved:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
});
} }
if (isLoaded){ if (isLoaded){
@@ -240,9 +247,11 @@ namespace TweetDuck.Core{
} }
} }
private static void plugins_Executed(object sender, PluginErrorEventArgs e){ private void plugins_Executed(object sender, PluginErrorEventArgs e){
if (e.HasErrors){ if (e.HasErrors){
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n"+string.Join("\n\n", e.Errors), FormMessage.OK); this.InvokeAsyncSafe(() => {
FormMessage.Error("Error Executing Plugins", "Failed to execute the following plugins:\n\n" + string.Join("\n\n", e.Errors), FormMessage.OK);
});
} }
} }
@@ -282,7 +291,7 @@ namespace TweetDuck.Core{
UpdateInstallerPath = update.InstallerPath; UpdateInstallerPath = update.InstallerPath;
ForceClose(); ForceClose();
} }
else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: "+(update.DownloadError?.Message ?? "unknown error")+"\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){ else if (status != UpdateDownloadStatus.Canceled && FormMessage.Error("Update Has Failed", "Could not automatically download the update: " + (update.DownloadError?.Message ?? "unknown error") + "\n\nWould you like to open the website and try downloading the update manually?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(Program.Website); BrowserUtils.OpenExternalBrowser(Program.Website);
ForceClose(); ForceClose();
} }
@@ -315,10 +324,6 @@ namespace TweetDuck.Core{
} }
} }
private void updateBridge_UpdateDelayed(object sender, UpdateInfo update){
// stops the timer
}
private void updateBridge_UpdateDismissed(object sender, UpdateInfo update){ private void updateBridge_UpdateDismissed(object sender, UpdateInfo update){
Config.DismissedUpdate = update.VersionTag; Config.DismissedUpdate = update.VersionTag;
Config.Save(); Config.Save();
@@ -326,7 +331,9 @@ namespace TweetDuck.Core{
protected override void WndProc(ref Message m){ protected override void WndProc(ref Message m){
if (isLoaded && m.Msg == Program.WindowRestoreMessage){ if (isLoaded && m.Msg == Program.WindowRestoreMessage){
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){ using Process me = Process.GetCurrentProcess();
if (me.Id == m.WParam.ToInt32()){
trayIcon_ClickRestore(trayIcon, EventArgs.Empty); trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
} }
@@ -363,14 +370,7 @@ namespace TweetDuck.Core{
} }
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
#if DEBUG Program.Resources.OnReloadTriggered();
ScriptLoader.HotSwap();
#else
if (ModifierKeys.HasFlag(Keys.Shift)){
ScriptLoader.ClearCache();
}
#endif
ignoreUpdateCheckError = false; ignoreUpdateCheckError = false;
browser.ReloadToTweetDeck(); browser.ReloadToTweetDeck();
AnalyticsFile.BrowserReloads.Trigger(); AnalyticsFile.BrowserReloads.Trigger();
@@ -501,12 +501,8 @@ 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 (!ContainsFocus){ if (Config.EnableTrayHighlight && !ContainsFocus){
if (Config.EnableTrayHighlight){ trayIcon.HasNotifications = true;
trayIcon.HasNotifications = true;
}
taskbarIcon.HasNotifications = false;
} }
} }
@@ -514,24 +510,43 @@ namespace TweetDuck.Core{
AnalyticsFile.SoundNotifications.Trigger(); AnalyticsFile.SoundNotifications.Trigger();
} }
public void PlayVideo(string url, string username){ public void PlayVideo(string videoUrl, string tweetUrl, string username, IJavascriptCallback callShowOverlay){
if (string.IsNullOrEmpty(url)){ string playerPath = Config.VideoPlayerPath;
videoPlayer?.Close();
return; if (playerPath == null || !File.Exists(playerPath)){
if (videoPlayer == null){
videoPlayer = new VideoPlayer(this);
videoPlayer.ProcessExited += (sender, args) => {
browser.HideVideoOverlay(true);
};
}
callShowOverlay.ExecuteAsync();
callShowOverlay.Dispose();
videoPlayer.Launch(videoUrl, tweetUrl, username);
} }
else{
callShowOverlay.Dispose();
if (videoPlayer == null){ string quotedUrl = '"' + videoUrl + '"';
videoPlayer = new VideoPlayer(this); string playerArgs = Config.VideoPlayerPathArgs == null ? quotedUrl : Config.VideoPlayerPathArgs + ' ' + quotedUrl;
videoPlayer.ProcessExited += (sender, args) => { try{
browser.HideVideoOverlay(true); using(Process.Start(playerPath, playerArgs)){}
}; }catch(Exception e){
Program.Reporter.HandleException("Error Opening Video Player", "Could not open the video player.", true, e);
}
} }
videoPlayer.Launch(url, username);
AnalyticsFile.VideoPlays.Trigger(); AnalyticsFile.VideoPlays.Trigger();
} }
public void StopVideo(){
videoPlayer?.Close();
}
public bool ProcessBrowserKey(Keys key){ public bool ProcessBrowserKey(Keys key){
if (videoPlayer != null && videoPlayer.Running){ if (videoPlayer != null && videoPlayer.Running){
videoPlayer.SendKeyEvent(key); videoPlayer.SendKeyEvent(key);

View File

@@ -17,8 +17,6 @@ namespace TweetDuck.Core{
else return false; else return false;
} }
public static bool HasAnyDialogs => Application.OpenForms.OfType<IAppDialog>().Any();
public static void CloseAllDialogs(){ public static void CloseAllDialogs(){
foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){ foreach(IAppDialog dialog in Application.OpenForms.OfType<IAppDialog>().Reverse()){
((Form)dialog).Close(); ((Form)dialog).Close();

View File

@@ -6,18 +6,20 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using System.Linq; using System.Linq;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Adapters;
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.Other.Analytics; using TweetDuck.Core.Other.Analytics;
using TweetDuck.Resources; using TweetLib.Core.Features.Twitter;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
protected static UserConfig Config => Program.Config.User; public static ContextInfo CurrentInfo { get; } = new ContextInfo();
private static TwitterUtils.ImageQuality ImageQuality => Config.TwitterImageQuality; protected static UserConfig Config => Program.Config.User;
private static 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;
@@ -40,11 +42,11 @@ namespace TweetDuck.Core.Handling{
} }
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 (!TwitterUrls.IsTweetDeck(frame.Url) || browser.IsLoading){
Context = TweetDeckBridge.ContextInfo.Reset(); Context = CurrentInfo.Reset();
} }
else{ else{
Context = TweetDeckBridge.ContextInfo.Create(parameters); Context = CurrentInfo.Create(parameters);
} }
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
@@ -53,13 +55,13 @@ namespace TweetDuck.Core.Handling{
model.AddItem(MenuReadApplyROT13, "Apply ROT13"); model.AddItem(MenuReadApplyROT13, "Apply ROT13");
model.AddSeparator(); model.AddSeparator();
} }
string TextOpen(string name) => "Open "+name+" in browser"; static string TextOpen(string name) => "Open " + name + " in browser";
string TextCopy(string name) => "Copy "+name+" address"; static string TextCopy(string name) => "Copy " + name + " address";
string TextSave(string name) => "Save "+name+" as..."; static string TextSave(string name) => "Save " + name + " as...";
if (Context.Types.HasFlag(ContextInfo.ContextType.Link) && !Context.UnsafeLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){ if (Context.Types.HasFlag(ContextInfo.ContextType.Link) && !Context.UnsafeLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
if (TwitterUtils.RegexAccount.IsMatch(Context.UnsafeLinkUrl)){ if (TwitterUrls.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");
@@ -78,7 +80,7 @@ namespace TweetDuck.Core.Handling{
model.AddItem(MenuSaveMedia, TextSave("video")); model.AddItem(MenuSaveMedia, TextSave("video"));
model.AddSeparator(); model.AddSeparator();
} }
else if (Context.Types.HasFlag(ContextInfo.ContextType.Image) && Context.MediaUrl != TweetNotification.AppLogo.Url){ else if (Context.Types.HasFlag(ContextInfo.ContextType.Image) && Context.MediaUrl != FormNotificationBase.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"));
@@ -106,7 +108,7 @@ namespace TweetDuck.Core.Handling{
case MenuCopyUsername: { case MenuCopyUsername: {
string url = Context.UnsafeLinkUrl; string url = Context.UnsafeLinkUrl;
Match match = TwitterUtils.RegexAccount.Match(url); Match match = TwitterUrls.RegexAccount.Match(url);
SetClipboardText(control, match.Success ? match.Groups[1].Value : url); SetClipboardText(control, match.Success ? match.Groups[1].Value : url);
control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger); control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger);
@@ -114,11 +116,11 @@ namespace TweetDuck.Core.Handling{
} }
case MenuOpenMediaUrl: case MenuOpenMediaUrl:
OpenBrowser(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); OpenBrowser(control, TwitterUrls.GetMediaLink(Context.MediaUrl, ImageQuality));
break; break;
case MenuCopyMediaUrl: case MenuCopyMediaUrl:
SetClipboardText(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); SetClipboardText(control, TwitterUrls.GetMediaLink(Context.MediaUrl, ImageQuality));
break; break;
case MenuViewImage: { case MenuViewImage: {
@@ -176,7 +178,7 @@ namespace TweetDuck.Core.Handling{
break; break;
case MenuOpenDevTools: case MenuOpenDevTools:
browserControl.ShowDevTools(); browserControl.OpenDevToolsCustom();
break; break;
} }
@@ -184,7 +186,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){
Context = TweetDeckBridge.ContextInfo.Reset(); Context = CurrentInfo.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){
@@ -192,7 +194,7 @@ namespace TweetDuck.Core.Handling{
} }
protected static void DeselectAll(IFrame frame){ protected static void DeselectAll(IFrame frame){
ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); CefScriptExecutor.RunScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect");
} }
protected static void OpenBrowser(Control control, string url){ protected static void OpenBrowser(Control control, string url){
@@ -204,7 +206,7 @@ namespace TweetDuck.Core.Handling{
} }
protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){ protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){
model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser)+1, insertCommand, insertLabel); model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser) + 1, insertCommand, insertLabel);
} }
protected static void AddDebugMenuItems(IMenuModel model){ protected static void AddDebugMenuItems(IMenuModel model){
@@ -215,13 +217,13 @@ namespace TweetDuck.Core.Handling{
} }
protected static void RemoveSeparatorIfLast(IMenuModel model){ protected static void RemoveSeparatorIfLast(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){ if (model.Count > 0 && model.GetTypeAt(model.Count - 1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1); model.RemoveAt(model.Count - 1);
} }
} }
protected static void AddSeparator(IMenuModel model){ protected static void AddSeparator(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) != MenuItemType.Separator){ // do not add separators if there is nothing to separate if (model.Count > 0 && model.GetTypeAt(model.Count - 1) != MenuItemType.Separator){ // do not add separators if there is nothing to separate
model.AddSeparator(); model.AddSeparator();
} }
} }

View File

@@ -2,7 +2,7 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Utils; using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class ContextMenuBrowser : ContextMenuBase{ sealed class ContextMenuBrowser : ContextMenuBase{
@@ -24,7 +24,7 @@ namespace TweetDuck.Core.Handling{
private const string TitleMuteNotifications = "Mute notifications"; private const string TitleMuteNotifications = "Mute notifications";
private const string TitleSettings = "Options"; private const string TitleSettings = "Options";
private const string TitlePlugins = "Plugins"; private const string TitlePlugins = "Plugins";
private const string TitleAboutProgram = "About "+Program.BrandName; private const string TitleAboutProgram = "About " + Program.BrandName;
private readonly FormBrowser form; private readonly FormBrowser form;
@@ -53,7 +53,7 @@ namespace TweetDuck.Core.Handling{
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (isSelecting && !isEditing && TwitterUtils.IsTweetDeckWebsite(frame)){ if (isSelecting && !isEditing && TwitterUrls.IsTweetDeck(frame.Url)){
InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column"); InsertSelectionSearchItem(model, MenuSearchInColumn, "Search in a column");
} }

View File

@@ -25,7 +25,7 @@ namespace TweetDuck.Core.Handling.Filters{
int responseLength = responseData.Length; int responseLength = responseData.Length;
if (state == State.Reading){ if (state == State.Reading){
int bytesToRead = Math.Min(responseLength-offset, (int)Math.Min(dataIn?.Length ?? 0, int.MaxValue)); int bytesToRead = Math.Min(responseLength - offset, (int)Math.Min(dataIn?.Length ?? 0, int.MaxValue));
dataIn?.Read(responseData, offset, bytesToRead); dataIn?.Read(responseData, offset, bytesToRead);
offset += bytesToRead; offset += bytesToRead;
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Handling.Filters{
return FilterStatus.NeedMoreData; return FilterStatus.NeedMoreData;
} }
else if (state == State.Writing){ else if (state == State.Writing){
int bytesToWrite = Math.Min(responseLength-offset, (int)Math.Min(dataOut.Length, int.MaxValue)); int bytesToWrite = Math.Min(responseLength - offset, (int)Math.Min(dataOut.Length, int.MaxValue));
if (bytesToWrite > 0){ if (bytesToWrite > 0){
dataOut.Write(responseData, offset, bytesToWrite); dataOut.Write(responseData, offset, bytesToWrite);

View File

@@ -11,12 +11,11 @@ namespace TweetDuck.Core.Handling.General{
private static void UpdatePrefsInternal(){ private static void UpdatePrefsInternal(){
UserConfig config = Program.Config.User; UserConfig config = Program.Config.User;
using IRequestContext ctx = Cef.GetGlobalRequestContext();
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ ctx.SetPreference("browser.enable_spellchecking", config.EnableSpellCheck, out string _);
ctx.SetPreference("browser.enable_spellchecking", config.EnableSpellCheck, out string _); ctx.SetPreference("spellcheck.dictionary", config.SpellCheckLanguage, out string _);
ctx.SetPreference("spellcheck.dictionary", config.SpellCheckLanguage, out string _); ctx.SetPreference("settings.a11y.animation_policy", config.EnableAnimatedImages ? "allowed" : "none", out string _);
ctx.SetPreference("settings.a11y.animation_policy", config.EnableAnimatedImages ? "allowed" : "none", out string _);
}
} }
void IBrowserProcessHandler.OnContextInitialized(){ void IBrowserProcessHandler.OnContextInitialized(){

View File

@@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
@@ -9,7 +8,7 @@ namespace TweetDuck.Core.Handling.General{
sealed class FileDialogHandler : IDialogHandler{ sealed class FileDialogHandler : IDialogHandler{
public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){ public bool OnFileDialog(IWebBrowser browserControl, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback){
if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple){ if (mode == CefFileDialogMode.Open || mode == CefFileDialogMode.OpenMultiple){
string allFilters = string.Join(";", acceptFilters.Select(filter => "*"+filter)); string allFilters = string.Join(";", acceptFilters.SelectMany(ParseFileType).Where(filter => !string.IsNullOrEmpty(filter)).Select(filter => "*" + filter));
using(OpenFileDialog dialog = new OpenFileDialog{ using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
@@ -19,8 +18,8 @@ namespace TweetDuck.Core.Handling.General{
Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*" Filter = $"All Supported Formats ({allFilters})|{allFilters}|All Files (*.*)|*.*"
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
string ext = Path.GetExtension(dialog.FileName); string ext = Path.GetExtension(dialog.FileName)?.ToLower();
callback.Continue(acceptFilters.FindIndex(filter => filter.Equals(ext, StringComparison.OrdinalIgnoreCase)), dialog.FileNames.ToList()); callback.Continue(acceptFilters.FindIndex(filter => ParseFileType(filter).Contains(ext)), dialog.FileNames.ToList());
} }
else{ else{
callback.Cancel(); callback.Cancel();
@@ -36,5 +35,27 @@ namespace TweetDuck.Core.Handling.General{
return false; return false;
} }
} }
private static IEnumerable<string> ParseFileType(string type){
if (string.IsNullOrEmpty(type)){
return new string[0];
}
if (type[0] == '.'){
return new string[]{ type };
}
switch(type){
case "image/jpeg": return new string[]{ ".jpg", ".jpeg" };
case "image/png": return new string[]{ ".png" };
case "image/gif": return new string[]{ ".gif" };
case "image/webp": return new string[]{ ".webp" };
case "video/mp4": return new string[]{ ".mp4" };
case "video/quicktime": return new string[]{ ".mov", ".qt" };
}
System.Diagnostics.Debugger.Break();
return new string[0];
}
} }
} }

View File

@@ -12,15 +12,17 @@ namespace TweetDuck.Core.Handling.General{
int pipe = text.IndexOf('|'); int pipe = text.IndexOf('|');
if (pipe != -1){ if (pipe != -1){
switch(text.Substring(0, pipe)){ icon = text.Substring(0, pipe) switch{
case "error": icon = MessageBoxIcon.Error; break; "error" => MessageBoxIcon.Error,
case "warning": icon = MessageBoxIcon.Warning; break; "warning" => MessageBoxIcon.Warning,
case "info": icon = MessageBoxIcon.Information; break; "info" => MessageBoxIcon.Information,
case "question": icon = MessageBoxIcon.Question; break; "question" => MessageBoxIcon.Question,
default: return new FormMessage(caption, text, icon); _ => MessageBoxIcon.None
} };
text = text.Substring(pipe+1); if (icon != MessageBoxIcon.None){
text = text.Substring(pipe + 1);
}
} }
return new FormMessage(caption, text, icon); return new FormMessage(caption, text, icon);
@@ -51,13 +53,13 @@ namespace TweetDuck.Core.Handling.General{
input = new TextBox{ input = new TextBox{
Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom, Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom,
Font = SystemFonts.MessageBoxFont, Font = SystemFonts.MessageBoxFont,
Location = new Point(BrowserUtils.Scale(22+inputPad, dpiScale), form.ActionPanelY-BrowserUtils.Scale(46, dpiScale)), Location = new Point(BrowserUtils.Scale(22 + inputPad, dpiScale), form.ActionPanelY - BrowserUtils.Scale(46, dpiScale)),
Size = new Size(form.ClientSize.Width-BrowserUtils.Scale(44+inputPad, dpiScale), BrowserUtils.Scale(23, dpiScale)) Size = new Size(form.ClientSize.Width - BrowserUtils.Scale(44 + inputPad, dpiScale), BrowserUtils.Scale(23, dpiScale))
}; };
form.Controls.Add(input); form.Controls.Add(input);
form.ActiveControl = input; form.ActiveControl = input;
form.Height += input.Size.Height+input.Margin.Vertical; form.Height += input.Size.Height + input.Margin.Vertical;
} }
else{ else{
callback.Continue(false); callback.Continue(false);

View File

@@ -4,11 +4,15 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling.General{ namespace TweetDuck.Core.Handling.General{
sealed class LifeSpanHandler : ILifeSpanHandler{ sealed class LifeSpanHandler : ILifeSpanHandler{
private static bool IsPopupAllowed(string url){
return url.StartsWith("https://twitter.com/teams/authorize?");
}
public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){ public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){
switch(targetDisposition){ switch(targetDisposition){
case WindowOpenDisposition.NewBackgroundTab: case WindowOpenDisposition.NewBackgroundTab:
case WindowOpenDisposition.NewForegroundTab: case WindowOpenDisposition.NewForegroundTab:
case WindowOpenDisposition.NewPopup: case WindowOpenDisposition.NewPopup when !IsPopupAllowed(targetUrl):
case WindowOpenDisposition.NewWindow: case WindowOpenDisposition.NewWindow:
browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl)); browserControl.AsControl().InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(targetUrl));
return true; return true;

View File

@@ -9,7 +9,7 @@ namespace TweetDuck.Core.Handling{
protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){ protected virtual bool HandleRawKey(IWebBrowser browserControl, IBrowser browser, Keys key, CefEventFlags modifiers){
if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){ if (modifiers == (CefEventFlags.ControlDown | CefEventFlags.ShiftDown) && key == Keys.I){
if (BrowserUtils.HasDevTools){ if (BrowserUtils.HasDevTools){
browser.ShowDevTools(); browserControl.OpenDevToolsCustom();
} }
else{ else{
browserControl.AsControl().InvokeSafe(() => { browserControl.AsControl().InvokeSafe(() => {
@@ -22,7 +22,7 @@ namespace TweetDuck.Core.Handling{
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."; 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); FormMessage.Information("Dev Tools", "You do not have dev tools installed. " + extraMessage, FormMessage.OK);
}); });
} }

View File

@@ -7,10 +7,11 @@ using CefSharp;
using CefSharp.Handler; using CefSharp.Handler;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.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 Regex TweetDeckResourceUrl = new Regex(@"/dist/(.*?)\.(.*?)\.(css|js)$");
private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4); private static readonly SortedList<string, string> TweetDeckHashes = new SortedList<string, string>(4);
public static void LoadResourceRewriteRules(string rules){ public static void LoadResourceRewriteRules(string rules){
@@ -21,21 +22,13 @@ namespace TweetDuck.Core.Handling{
TweetDeckHashes.Clear(); TweetDeckHashes.Clear();
foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){ foreach(string rule in rules.Replace(" ", "").ToLower().Split(',')){
string[] split = rule.Split('='); var (key, hash) = StringUtils.SplitInTwo(rule, '=') ?? throw new ArgumentException("A rule must have one '=' character: " + rule);
if (split.Length == 2){ if (hash.All(chr => char.IsDigit(chr) || (chr >= 'a' && chr <= 'f'))){
string key = split[0]; TweetDeckHashes.Add(key, hash);
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{ else{
throw new ArgumentException("A rule must have exactly one '=' character: "+rule); throw new ArgumentException("Invalid hash characters: " + rule);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Handling.Filters; using TweetDuck.Core.Handling.Filters;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class RequestHandlerBrowser : RequestHandlerBase{ sealed class RequestHandlerBrowser : RequestHandlerBase{
@@ -15,7 +16,7 @@ namespace TweetDuck.Core.Handling{
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.MainFrame){ if (request.ResourceType == ResourceType.MainFrame){
if (request.Url.EndsWith("//twitter.com/")){ if (request.Url.EndsWith("//twitter.com/")){
request.Url = TwitterUtils.TweetDeckURL; // redirect plain twitter.com requests, fixes bugs with login 2FA request.Url = TwitterUrls.TweetDeck; // redirect plain twitter.com requests, fixes bugs with login 2FA
} }
} }
else if (request.ResourceType == ResourceType.Script){ else if (request.ResourceType == ResourceType.Script){
@@ -41,6 +42,9 @@ namespace TweetDuck.Core.Handling{
BlockNextUserNavUrl = string.Empty; BlockNextUserNavUrl = string.Empty;
return block; return block;
} }
else if (request.TransitionType.HasFlag(TransitionType.ForwardBack) && TwitterUrls.IsTweetDeck(frame.Url)){
return true;
}
return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect); return base.OnBeforeBrowse(browserControl, browser, frame, request, userGesture, isRedirect);
} }

View File

@@ -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.Config.System.ClearCacheThreshold*1024L*1024L){ if (CalculateCacheSize() >= Program.Config.System.ClearCacheThreshold * 1024L * 1024L){
SetClearOnExit(); SetClearOnExit();
} }
}catch(Exception){ }catch(Exception){

View File

@@ -1,6 +1,6 @@
using System; using System;
using CefSharp; using CefSharp;
using TweetDuck.Core.Utils; using TweetLib.Core.Utils;
namespace TweetDuck.Core.Management{ namespace TweetDuck.Core.Management{
sealed class ContextInfo{ sealed class ContextInfo{
@@ -107,7 +107,7 @@ namespace TweetDuck.Core.Management{
private string unsafeLinkUrl = string.Empty; private string unsafeLinkUrl = string.Empty;
private string mediaUrl = string.Empty; private string mediaUrl = string.Empty;
private ChirpInfo chirp = default(ChirpInfo); private ChirpInfo chirp = default;
public void AddContext(IContextMenuParams parameters){ public void AddContext(IContextMenuParams parameters){
ContextMenuType flags = parameters.TypeFlags; ContextMenuType flags = parameters.TypeFlags;

View File

@@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Data; using TweetLib.Core.Data;
using TweetDuck.Plugins; using TweetLib.Core.Features.Plugins;
using TweetDuck.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core.Management{ namespace TweetDuck.Core.Management{
sealed class ProfileManager{ sealed class ProfileManager{
@@ -49,7 +49,7 @@ namespace TweetDuck.Core.Management{
try{ try{
stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full); stream.WriteFile(new string[]{ "plugin.data", plugin.Identifier, path.Relative }, path.Full);
}catch(ArgumentOutOfRangeException e){ }catch(ArgumentOutOfRangeException e){
FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. "+e.Message, FormMessage.OK); FormMessage.Warning("Export Profile", "Could not include a plugin file in the export. " + e.Message, FormMessage.OK);
} }
} }
} }
@@ -73,28 +73,27 @@ namespace TweetDuck.Core.Management{
Items items = Items.None; Items items = Items.None;
try{ try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))){ using CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None));
string key; string key;
while((key = stream.SkipFile()) != null){ while((key = stream.SkipFile()) != null){
switch(key){ switch(key){
case "config": case "config":
items |= Items.UserConfig; items |= Items.UserConfig;
break; break;
case "system": case "system":
items |= Items.SystemConfig; items |= Items.SystemConfig;
break; break;
case "plugin.config": case "plugin.config":
case "plugin.data": case "plugin.data":
items |= Items.PluginData; items |= Items.PluginData;
break; break;
case "cookies": case "cookies":
items |= Items.Session; items |= Items.Session;
break; break;
}
} }
} }
}catch(Exception){ }catch(Exception){
@@ -140,7 +139,7 @@ namespace TweetDuck.Core.Management{
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true); entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
if (!plugins.IsPluginInstalled(value[0])){ if (!plugins.Plugins.Any(plugin => plugin.Identifier.Equals(value[0]))){
missingPlugins.Add(value[0]); missingPlugins.Add(value[0]);
} }
} }
@@ -158,7 +157,7 @@ namespace TweetDuck.Core.Management{
} }
if (missingPlugins.Count > 0){ if (missingPlugins.Count > 0){
FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n"+string.Join("\n", missingPlugins), FormMessage.OK); FormMessage.Information("Profile Import", "Detected missing plugins when importing plugin data:\n" + string.Join("\n", missingPlugins), FormMessage.OK);
} }
return true; return true;

View File

@@ -26,7 +26,7 @@ namespace TweetDuck.Core.Management{
this.owner.FormClosing += owner_FormClosing; this.owner.FormClosing += owner_FormClosing;
} }
public void Launch(string url, string username){ public void Launch(string videoUrl, string tweetUrl, string username){
if (Running){ if (Running){
Destroy(); Destroy();
isClosing = false; isClosing = false;
@@ -40,11 +40,11 @@ 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} {(int)Math.Floor(100F*owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{url}\" \"{pipe.GenerateToken()}\"", Arguments = $"{owner.Handle} {(int)Math.Floor(100F * owner.GetDPIScale())} {Config.VideoPlayerVolume} \"{videoUrl}\" \"{pipe.GenerateToken()}\"",
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true RedirectStandardOutput = true
})) != null){ })) != null){
currentInstance = new Instance(process, pipe, url, username); currentInstance = new Instance(process, pipe, videoUrl, tweetUrl, username);
process.EnableRaisingEvents = true; process.EnableRaisingEvents = true;
process.Exited += process_Exited; process.Exited += process_Exited;
@@ -81,7 +81,7 @@ namespace TweetDuck.Core.Management{
case "download": case "download":
if (currentInstance != null){ if (currentInstance != null){
owner.AnalyticsFile.DownloadedVideos.Trigger(); owner.AnalyticsFile.DownloadedVideos.Trigger();
TwitterUtils.DownloadVideo(currentInstance.Url, currentInstance.Username); TwitterUtils.DownloadVideo(currentInstance.VideoUrl, currentInstance.Username);
} }
break; break;
@@ -135,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.LogVerbose("[VideoPlayer] "+e.Data); Program.Reporter.LogVerbose("[VideoPlayer] " + e.Data);
} }
} }
@@ -145,7 +145,7 @@ namespace TweetDuck.Core.Management{
} }
int exitCode = currentInstance.Process.ExitCode; int exitCode = currentInstance.Process.ExitCode;
string url = currentInstance.Url; string tweetUrl = currentInstance.TweetUrl;
currentInstance.Dispose(); currentInstance.Dispose();
currentInstance = null; currentInstance = null;
@@ -153,14 +153,14 @@ namespace TweetDuck.Core.Management{
switch(exitCode){ switch(exitCode){
case 3: // CODE_LAUNCH_FAIL case 3: // CODE_LAUNCH_FAIL
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(url); BrowserUtils.OpenExternalBrowser(tweetUrl);
} }
break; break;
case 4: // CODE_MEDIA_ERROR case 4: // CODE_MEDIA_ERROR
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(url); BrowserUtils.OpenExternalBrowser(tweetUrl);
} }
break; break;
@@ -184,13 +184,15 @@ namespace TweetDuck.Core.Management{
public Process Process { get; } public Process Process { get; }
public DuplexPipe.Server Pipe { get; } public DuplexPipe.Server Pipe { get; }
public string Url { get; } public string VideoUrl { get; }
public string TweetUrl { get; }
public string Username { get; } public string Username { get; }
public Instance(Process process, DuplexPipe.Server pipe, string url, string username){ public Instance(Process process, DuplexPipe.Server pipe, string videoUrl, string tweetUrl, string username){
this.Process = process; this.Process = process;
this.Pipe = pipe; this.Pipe = pipe;
this.Url = url; this.VideoUrl = videoUrl;
this.TweetUrl = tweetUrl;
this.Username = username; this.Username = username;
} }

View File

@@ -2,17 +2,17 @@
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp; using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Plugins; using TweetLib.Core.Features.Notifications;
using TweetDuck.Resources; using TweetLib.Core.Features.Plugins;
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 => Config.NotificationPosition == TweetNotification.Position.Custom; protected override bool CanDragWindow => Config.NotificationPosition == DesktopNotification.Position.Custom;
protected override FormBorderStyle NotificationBorderStyle{ protected override FormBorderStyle NotificationBorderStyle{
get{ get{
if (Config.NotificationSize == TweetNotification.Size.Custom){ if (Config.NotificationSize == DesktopNotification.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,22 +23,22 @@ namespace TweetDuck.Core.Notification.Example{
} }
} }
protected override string BodyClasses => base.BodyClasses+" td-example"; protected override string BodyClasses => base.BodyClasses + " td-example";
public event EventHandler Ready; public event EventHandler Ready;
private readonly TweetNotification exampleNotification; private readonly DesktopNotification exampleNotification;
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
browser.LoadingStateChanged += browser_LoadingStateChanged; browser.LoadingStateChanged += browser_LoadingStateChanged;
string exampleTweetHTML = ScriptLoader.LoadResourceSilent("pages/example.html")?.Replace("{avatar}", TweetNotification.AppLogo.Url) ?? string.Empty; string exampleTweetHTML = Program.Resources.LoadSilent("pages/example.html")?.Replace("{avatar}", AppLogo.Url) ?? string.Empty;
#if DEBUG #if DEBUG
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 = new TweetNotification(string.Empty, string.Empty, "Home", exampleTweetHTML, 176, string.Empty, string.Empty); exampleNotification = new DesktopNotification(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){

View File

@@ -1,28 +1,34 @@
using CefSharp.WinForms; using CefSharp.WinForms;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Analytics; using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{ abstract partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
protected static UserConfig Config => Program.Config.User; public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
public static string FontSize = null;
public static string HeadLayout = null;
protected static UserConfig Config => Program.Config.User;
protected static int FontSizeLevel{ protected static int FontSizeLevel{
get{ get => FontSize switch{
switch(TweetDeckBridge.FontSize){ "largest" => 4,
case "largest": return 4; "large" => 3,
case "large": return 3; "small" => 1,
case "small": return 1; "smallest" => 0,
case "smallest": return 0; _ => 2
default: return 2; };
}
}
} }
protected virtual Point PrimaryLocation{ protected virtual Point PrimaryLocation{
@@ -30,7 +36,7 @@ namespace TweetDuck.Core.Notification{
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);
@@ -39,21 +45,21 @@ namespace TweetDuck.Core.Notification{
int edgeDist = Config.NotificationEdgeDistance; int edgeDist = Config.NotificationEdgeDistance;
switch(Config.NotificationPosition){ switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: case DesktopNotification.Position.TopLeft:
return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist); return new Point(screen.WorkingArea.X + edgeDist, screen.WorkingArea.Y + edgeDist);
case TweetNotification.Position.TopRight: case DesktopNotification.Position.TopRight:
return new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist); return new Point(screen.WorkingArea.X + screen.WorkingArea.Width - edgeDist - Width, screen.WorkingArea.Y + edgeDist);
case TweetNotification.Position.BottomLeft: case DesktopNotification.Position.BottomLeft:
return new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height); return new Point(screen.WorkingArea.X + edgeDist, screen.WorkingArea.Y + screen.WorkingArea.Height - edgeDist - Height);
case TweetNotification.Position.BottomRight: case DesktopNotification.Position.BottomRight:
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 DesktopNotification.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();
} }
@@ -94,14 +100,14 @@ 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*Config.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;
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification(); private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
private TweetNotification currentNotification; private DesktopNotification currentNotification;
private int pauseCounter; private int pauseCounter;
public string CurrentTweetUrl => currentNotification?.TweetUrl; public string CurrentTweetUrl => currentNotification?.TweetUrl;
@@ -122,8 +128,8 @@ namespace TweetDuck.Core.Notification{
this.owner.FormClosed += owner_FormClosed; this.owner.FormClosed += owner_FormClosed;
ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory(); ResourceHandlerFactory resourceHandlerFactory = new ResourceHandlerFactory();
resourceHandlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); resourceHandlerFactory.RegisterHandler(TwitterUrls.TweetDeck, this.resourceHandler);
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo); resourceHandlerFactory.RegisterHandler(AppLogo);
this.browser = new ChromiumWebBrowser("about:blank"){ this.browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this, enableContextMenu), MenuHandler = new ContextMenuNotification(this, enableContextMenu),
@@ -188,13 +194,13 @@ namespace TweetDuck.Core.Notification{
} }
} }
protected abstract string GetTweetHTML(TweetNotification tweet); protected abstract string GetTweetHTML(DesktopNotification tweet);
protected virtual void LoadTweet(TweetNotification tweet){ protected virtual void LoadTweet(DesktopNotification tweet){
currentNotification = tweet; currentNotification = tweet;
resourceHandler.SetHTML(GetTweetHTML(tweet)); resourceHandler.SetHTML(GetTweetHTML(tweet));
browser.Load(TwitterUtils.TweetDeckURL); browser.Load(TwitterUrls.TweetDeck);
DisplayTooltip(null); DisplayTooltip(null);
} }

View File

@@ -2,14 +2,16 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Adapters;
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.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetLib.Core.Data;
using TweetDuck.Resources; using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
abstract partial class FormNotificationMain : FormNotificationBase{ abstract partial class FormNotificationMain : FormNotificationBase{
@@ -44,32 +46,22 @@ namespace TweetDuck.Core.Notification{
} }
private int BaseClientWidth{ private int BaseClientWidth{
get{ get => Config.NotificationSize switch{
switch(Config.NotificationSize){ DesktopNotification.Size.Custom => Config.CustomNotificationSize.Width,
default: _ => BrowserUtils.Scale(284, SizeScale * (1.0 + 0.05 * FontSizeLevel))
return BrowserUtils.Scale(284, SizeScale*(1.0+0.05*FontSizeLevel)); };
case TweetNotification.Size.Custom:
return Config.CustomNotificationSize.Width;
}
}
} }
private int BaseClientHeight{ private int BaseClientHeight{
get{ get => Config.NotificationSize switch{
switch(Config.NotificationSize){ DesktopNotification.Size.Custom => Config.CustomNotificationSize.Height,
default: _ => BrowserUtils.Scale(122, SizeScale * (1.0 + 0.08 * FontSizeLevel))
return BrowserUtils.Scale(122, SizeScale*(1.0+0.08*FontSizeLevel)); };
case TweetNotification.Size.Custom:
return Config.CustomNotificationSize.Height;
}
}
} }
protected virtual string BodyClasses => IsCursorOverBrowser ? "td-notification td-hover" : "td-notification"; protected virtual string BodyClasses => IsCursorOverBrowser ? "td-notification td-hover" : "td-notification";
public Size BrowserSize => Config.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize; 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();
@@ -83,7 +75,7 @@ namespace TweetDuck.Core.Notification{
browser.LoadingStateChanged += Browser_LoadingStateChanged; browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd; browser.FrameLoadEnd += Browser_FrameLoadEnd;
plugins.Register(browser, PluginEnvironment.Notification, this); plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser));
mouseHookDelegate = MouseHookProc; mouseHookDelegate = MouseHookProc;
Disposed += (sender, args) => StopMouseHook(true); Disposed += (sender, args) => StopMouseHook(true);
@@ -110,7 +102,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), Config.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)){
@@ -164,7 +156,7 @@ namespace TweetDuck.Core.Notification{
if (frame.IsMain && browser.Address != "about:blank"){ if (frame.IsMain && browser.Address != "about:blank"){
frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification)); frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Notification));
ScriptLoader.ExecuteFile(frame, "notification.js", this); CefScriptExecutor.RunFile(frame, "notification.js");
} }
} }
@@ -174,14 +166,23 @@ namespace TweetDuck.Core.Notification{
} }
private void timerHideProgress_Tick(object sender, EventArgs e){ private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen){ bool isCursorInside = Bounds.Contains(Cursor.Position);
if (isCursorInside){
StartMouseHook();
}
else{
StopMouseHook(false);
}
if (isCursorInside || FreezeTimer || ContextMenuOpen){
return; return;
} }
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(Config.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value); progressBarTimer.SetValueInstant(Config.NotificationTimerCountDown ? progressBarTimer.Maximum - value : value);
if (timeLeft <= 0){ if (timeLeft <= 0){
FinishCurrentNotification(); FinishCurrentNotification();
@@ -190,7 +191,7 @@ namespace TweetDuck.Core.Notification{
// notification methods // notification methods
public virtual void ShowNotification(TweetNotification notification){ public virtual void ShowNotification(DesktopNotification notification){
LoadTweet(notification); LoadTweet(notification);
} }
@@ -227,8 +228,8 @@ namespace TweetDuck.Core.Notification{
} }
} }
protected override string GetTweetHTML(TweetNotification tweet){ protected override string GetTweetHTML(DesktopNotification tweet){
string html = tweet.GenerateHtml(BodyClasses, this); string html = tweet.GenerateHtml(BodyClasses, HeadLayout, Config.CustomNotificationCSS);
foreach(InjectedHTML injection in plugins.NotificationInjections){ foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.InjectInto(html); html = injection.InjectInto(html);
@@ -237,7 +238,7 @@ namespace TweetDuck.Core.Notification{
return html; return html;
} }
protected override void LoadTweet(TweetNotification tweet){ protected override void LoadTweet(DesktopNotification tweet){
timerProgress.Stop(); timerProgress.Stop();
totalTime = timeLeft = tweet.GetDisplayDuration(Config.NotificationDurationValue); totalTime = timeLeft = tweet.GetDisplayDuration(Config.NotificationDurationValue);
progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum; progressBarTimer.Value = Config.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
@@ -247,7 +248,7 @@ namespace TweetDuck.Core.Notification{
protected override void SetNotificationSize(int width, int height){ protected override void SetNotificationSize(int width, int height){
if (Config.DisplayNotificationTimer){ if (Config.DisplayNotificationTimer){
ClientSize = new Size(width, height+timerBarHeight); ClientSize = new Size(width, height + timerBarHeight);
progressBarTimer.Visible = true; progressBarTimer.Visible = true;
} }
else{ else{
@@ -265,7 +266,6 @@ namespace TweetDuck.Core.Notification{
} }
MoveToVisibleLocation(); MoveToVisibleLocation();
StartMouseHook();
} }
protected virtual void OnNotificationReady(){ protected virtual void OnNotificationReady(){

View File

@@ -1,9 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using TweetDuck.Plugins;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed partial class FormNotificationTweet : FormNotificationMain{ sealed partial class FormNotificationTweet : FormNotificationMain{
@@ -25,7 +26,7 @@ namespace TweetDuck.Core.Notification{
} }
} }
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4); private readonly Queue<DesktopNotification> tweetQueue = new Queue<DesktopNotification>(4);
private bool needsTrim; private bool needsTrim;
private bool hasTemporarilyMoved; private bool hasTemporarilyMoved;
@@ -81,7 +82,7 @@ namespace TweetDuck.Core.Notification{
// notification methods // notification methods
public override void ShowNotification(TweetNotification notification){ public override void ShowNotification(DesktopNotification notification){
tweetQueue.Enqueue(notification); tweetQueue.Enqueue(notification);
if (!IsPaused){ if (!IsPaused){
@@ -153,7 +154,7 @@ namespace TweetDuck.Core.Notification{
base.UpdateTitle(); base.UpdateTitle();
if (tweetQueue.Count > 0){ if (tweetQueue.Count > 0){
Text = Text+" ("+tweetQueue.Count+" more left)"; Text = Text + " (" + tweetQueue.Count + " more left)";
} }
} }

View File

@@ -3,12 +3,13 @@ using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp; using CefSharp;
using TweetDuck.Core.Adapters;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetLib.Core.Data;
using TweetDuck.Plugins; using TweetLib.Core.Features.Notifications;
using TweetDuck.Resources; using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
sealed class FormNotificationScreenshotable : FormNotificationBase{ sealed class FormNotificationScreenshotable : FormNotificationBase{
@@ -29,24 +30,23 @@ namespace TweetDuck.Core.Notification.Screenshot{
return; return;
} }
string script = ScriptLoader.LoadResourceSilent("screenshot.js"); string script = Program.Resources.LoadSilent("screenshot.js");
if (script == null){ if (script == null){
this.InvokeAsyncSafe(callback); this.InvokeAsyncSafe(callback);
return; return;
} }
using(IFrame frame = args.Browser.MainFrame){ using IFrame frame = args.Browser.MainFrame;
ScriptLoader.ExecuteScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot"); CefScriptExecutor.RunScript(frame, script.Replace("{width}", realWidth.ToString()).Replace("{frames}", TweetScreenshotManager.WaitFrames.ToString()), "gen:screenshot");
}
}; };
SetNotificationSize(realWidth, 1024); SetNotificationSize(realWidth, 1024);
LoadTweet(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty)); LoadTweet(new DesktopNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty));
} }
protected override string GetTweetHTML(TweetNotification tweet){ protected override string GetTweetHTML(DesktopNotification tweet){
string html = tweet.GenerateHtml("td-screenshot", this); string html = tweet.GenerateHtml("td-screenshot", HeadLayout, Config.CustomNotificationCSS);
foreach(InjectedHTML injection in plugins.NotificationInjections){ foreach(InjectedHTML injection in plugins.NotificationInjections){
html = injection.InjectInto(html); html = injection.InjectInto(html);

View File

@@ -1,8 +1,10 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
[SuppressMessage("ReSharper", "UnusedMember.Global")]
sealed class ScreenshotBridge{ sealed class ScreenshotBridge{
private readonly Control owner; private readonly Control owner;

View File

@@ -9,7 +9,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Plugins; using TweetLib.Core.Features.Plugins;
#if GEN_SCREENSHOT_FRAMES #if GEN_SCREENSHOT_FRAMES
using System.Drawing.Imaging; using System.Drawing.Imaging;
@@ -134,9 +134,9 @@ namespace TweetDuck.Core.Notification.Screenshot{
private void debugger_Tick(object sender, EventArgs e){ private void debugger_Tick(object sender, EventArgs e){
if (frameCounter < 63 && screenshot.TakeScreenshot(true)){ if (frameCounter < 63 && screenshot.TakeScreenshot(true)){
try{ try{
Clipboard.GetImage()?.Save(Path.Combine(DebugScreenshotPath, "frame_"+(++frameCounter)+".png"), ImageFormat.Png); Clipboard.GetImage()?.Save(Path.Combine(DebugScreenshotPath, "frame_" + (++frameCounter) + ".png"), ImageFormat.Png);
}catch{ }catch{
System.Diagnostics.Debug.WriteLine("Failed generating frame "+frameCounter); System.Diagnostics.Debug.WriteLine("Failed generating frame " + frameCounter);
} }
} }
else{ else{

View File

@@ -11,18 +11,16 @@ namespace TweetDuck.Core.Notification{
public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm"; public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm";
public static IResourceHandler CreateFileHandler(string path){ public static IResourceHandler CreateFileHandler(string path){
string mimeType; string mimeType = Path.GetExtension(path) switch{
".weba" => "audio/webm",
switch(Path.GetExtension(path)){ ".webm" => "audio/webm",
case ".weba": ".wav" => "audio/wav",
case ".webm": mimeType = "audio/webm"; break; ".ogg" => "audio/ogg",
case ".wav": mimeType = "audio/wav"; break; ".mp3" => "audio/mp3",
case ".ogg": mimeType = "audio/ogg"; break; ".flac" => "audio/flac",
case ".mp3": mimeType = "audio/mp3"; break; ".opus" => "audio/ogg; codecs=opus",
case ".flac": mimeType = "audio/flac"; break; _ => null
case ".opus": mimeType = "audio/ogg; codecs=opus"; break; };
default: mimeType = null; break;
}
try{ try{
return ResourceHandler.FromFilePath(path, mimeType); return ResourceHandler.FromFilePath(path, mimeType);
@@ -30,12 +28,12 @@ namespace TweetDuck.Core.Notification{
FormBrowser browser = FormManager.TryFind<FormBrowser>(); FormBrowser browser = FormManager.TryFind<FormBrowser>();
browser?.InvokeAsyncSafe(() => { browser?.InvokeAsyncSafe(() => {
using(FormMessage form = new FormMessage("Sound Notification Error", "Could not find custom notification sound file:\n"+path, MessageBoxIcon.Error)){ using(FormMessage form = new FormMessage("Sound Notification Error", "Could not find custom notification sound file:\n" + path, MessageBoxIcon.Error)){
form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused); form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused);
Button btnViewOptions = form.AddButton("View Options"); Button btnViewOptions = form.AddButton("View Options");
btnViewOptions.Width += 16; btnViewOptions.Width += 16;
btnViewOptions.Location = new Point(btnViewOptions.Location.X-16, btnViewOptions.Location.Y); btnViewOptions.Location = new Point(btnViewOptions.Location.X - 16, btnViewOptions.Location.Y);
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnViewOptions){ if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnViewOptions){
browser.OpenSettings(typeof(TabSettingsSounds)); browser.OpenSettings(typeof(TabSettingsSounds));

View File

@@ -2,7 +2,8 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using TweetDuck.Data.Serialization; using TweetLib.Core.Serialization;
using TweetLib.Core.Serialization.Converters;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")] [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]

View File

@@ -8,7 +8,9 @@ using System.Threading.Tasks;
using System.Timers; using System.Timers;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetLib.Core;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
sealed class AnalyticsManager : IDisposable{ sealed class AnalyticsManager : IDisposable{
@@ -20,7 +22,7 @@ namespace TweetDuck.Core.Other.Analytics{
#else #else
"https://tweetduck.chylex.com/breadcrumb/report" "https://tweetduck.chylex.com/breadcrumb/report"
#endif #endif
); );
public AnalyticsFile File { get; } public AnalyticsFile File { get; }
@@ -80,7 +82,7 @@ namespace TweetDuck.Core.Other.Analytics{
private void SetLastDataCollectionTime(DateTime dt, string message = null){ private void SetLastDataCollectionTime(DateTime dt, string message = null){
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind); File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
File.LastCollectionVersion = Program.VersionTag; File.LastCollectionVersion = Program.VersionTag;
File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture); File.LastCollectionMessage = message ?? dt.ToString("g", Lib.Culture);
File.Save(); File.Save();
RestartTimer(); RestartTimer();
@@ -88,9 +90,9 @@ namespace TweetDuck.Core.Other.Analytics{
private void RestartTimer(){ private void RestartTimer(){
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection); TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes)); int minutesTillNext = (int)(CollectionInterval.TotalMinutes - Math.Floor(diff.TotalMinutes));
currentTimer.Interval = Math.Max(minutesTillNext, 2)*60000; currentTimer.Interval = Math.Max(minutesTillNext, 2) * 60000;
currentTimer.Start(); currentTimer.Start();
} }
@@ -117,7 +119,7 @@ namespace TweetDuck.Core.Other.Analytics{
System.Diagnostics.Debugger.Break(); System.Diagnostics.Debugger.Break();
#endif #endif
BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection()); WebUtils.NewClient(BrowserUtils.UserAgentVanilla).UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
}).ContinueWith(task => browser.InvokeAsyncSafe(() => { }).ContinueWith(task => browser.InvokeAsyncSafe(() => {
if (task.Status == TaskStatus.RanToCompletion){ if (task.Status == TaskStatus.RanToCompletion){
SetLastDataCollectionTime(DateTime.Now); SetLastDataCollectionTime(DateTime.Now);
@@ -137,7 +139,7 @@ namespace TweetDuck.Core.Other.Analytics{
case WebExceptionStatus.ProtocolError: case WebExceptionStatus.ProtocolError:
HttpWebResponse response = e.Response as HttpWebResponse; HttpWebResponse response = e.Response as HttpWebResponse;
message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)"); message = "HTTP Error " + (response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)");
break; break;
} }
@@ -150,7 +152,7 @@ namespace TweetDuck.Core.Other.Analytics{
#endif #endif
} }
ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message)); ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: " + (task.Exception.InnerException?.Message ?? task.Exception.Message));
} }
})); }));
} }

View File

@@ -47,7 +47,7 @@ namespace TweetDuck.Core.Other.Analytics{
build.AppendLine(); build.AppendLine();
} }
else{ else{
build.AppendLine(entry.Key+": "+entry.Value); build.AppendLine(entry.Key + ": " + entry.Value);
} }
} }

View File

@@ -8,10 +8,12 @@ using TweetDuck.Configuration;
using System.Linq; using System.Linq;
using System.Management; using System.Management;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetLib.Core;
using TweetDuck.Plugins.Enums; using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
static class AnalyticsReportGenerator{ static class AnalyticsReportGenerator{
@@ -27,7 +29,7 @@ namespace TweetDuck.Core.Other.Analytics{
{ "System Edition" , SystemEdition }, { "System Edition" , SystemEdition },
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" }, { "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
{ "System Build" , SystemBuild }, { "System Build" , SystemBuild },
{ "System Locale" , Program.Culture.Name.ToLower() }, { "System Locale" , Lib.Culture.Name.ToLower() },
0, 0,
{ "RAM" , Exact(RamSize) }, { "RAM" , Exact(RamSize) },
{ "GPU" , GpuVendor }, { "GPU" , GpuVendor },
@@ -79,7 +81,7 @@ namespace TweetDuck.Core.Other.Analytics{
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) }, { "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
0, 0,
{ "Plugins All" , List(plugins.Plugins.Select(Plugin)) }, { "Plugins All" , List(plugins.Plugins.Select(Plugin)) },
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(Plugin)) }, { "Plugins Enabled" , List(plugins.Plugins.Where(plugins.Config.IsEnabled).Select(Plugin)) },
0, 0,
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") }, { "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") }, { "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
@@ -124,9 +126,9 @@ namespace TweetDuck.Core.Other.Analytics{
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();
private static string RoundUp(int value, int multiple) => (multiple*(int)Math.Ceiling((double)value/multiple)).ToString(); private static string RoundUp(int value, int multiple) => (multiple * (int)Math.Ceiling((double)value / multiple)).ToString();
private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString(); private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort()+plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length); private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort() + plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length);
private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def; private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)")); private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));
@@ -141,19 +143,19 @@ namespace TweetDuck.Core.Other.Analytics{
string osName, osEdition, osBuild; string osName, osEdition, osBuild;
try{ try{
using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){ using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false);
// ReSharper disable once PossibleNullReferenceException
osName = key.GetValue("ProductName") as string;
osBuild = key.GetValue("CurrentBuild") as string;
osEdition = null;
if (osName != null){
Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");
if (match.Success){ // ReSharper disable once PossibleNullReferenceException
osName = match.Groups[1].Value; osName = key.GetValue("ProductName") as string;
osEdition = match.Groups[2].Value; osBuild = key.GetValue("CurrentBuild") as string;
} osEdition = null;
if (osName != null){
Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");
if (match.Success){
osName = match.Groups[1].Value;
osEdition = match.Groups[2].Value;
} }
} }
}catch{ }catch{
@@ -165,10 +167,10 @@ namespace TweetDuck.Core.Other.Analytics{
SystemBuild = osBuild ?? "(unknown)"; SystemBuild = osBuild ?? "(unknown)";
try{ try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){ using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory");
foreach(ManagementBaseObject obj in searcher.Get()){
RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L)); foreach(ManagementBaseObject obj in searcher.Get()){
} RamSize += (int)((ulong)obj["Capacity"] / (1024L * 1024L));
} }
}catch{ }catch{
RamSize = 0; RamSize = 0;
@@ -177,13 +179,13 @@ namespace TweetDuck.Core.Other.Analytics{
string gpu = null; string gpu = null;
try{ try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){ using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController");
foreach(ManagementBaseObject obj in searcher.Get()){
string vendor = obj["Caption"] as string;
if (!string.IsNullOrEmpty(vendor)){ foreach(ManagementBaseObject obj in searcher.Get()){
gpu = vendor; string vendor = obj["Caption"] as string;
}
if (!string.IsNullOrEmpty(vendor)){
gpu = vendor;
} }
} }
}catch{ }catch{
@@ -204,36 +206,30 @@ namespace TweetDuck.Core.Other.Analytics{
} }
private static string TrayMode{ private static string TrayMode{
get{ get => UserConfig.TrayBehavior switch{
switch(UserConfig.TrayBehavior){ TrayIcon.Behavior.DisplayOnly => "icon",
case TrayIcon.Behavior.DisplayOnly: return "icon"; TrayIcon.Behavior.MinimizeToTray => "minimize",
case TrayIcon.Behavior.MinimizeToTray: return "minimize"; TrayIcon.Behavior.CloseToTray => "close",
case TrayIcon.Behavior.CloseToTray: return "close"; TrayIcon.Behavior.Combined => "combined",
case TrayIcon.Behavior.Combined: return "combined"; _ => "off"
default: return "off"; };
}
}
} }
private static string NotificationPosition{ private static string NotificationPosition{
get{ get => UserConfig.NotificationPosition switch{
switch(UserConfig.NotificationPosition){ DesktopNotification.Position.TopLeft => "top left",
case TweetNotification.Position.TopLeft: return "top left"; DesktopNotification.Position.TopRight => "top right",
case TweetNotification.Position.TopRight: return "top right"; DesktopNotification.Position.BottomLeft => "bottom left",
case TweetNotification.Position.BottomLeft: return "bottom left"; DesktopNotification.Position.BottomRight => "bottom right",
case TweetNotification.Position.BottomRight: return "bottom right"; _ => "custom"
default: return "custom"; };
}
}
} }
private static string NotificationSize{ private static string NotificationSize{
get{ get => UserConfig.NotificationSize switch{
switch(UserConfig.NotificationSize){ DesktopNotification.Size.Auto => "auto",
case TweetNotification.Size.Auto: return "auto"; _ => RoundUp(UserConfig.CustomNotificationSize.Width, 20) + "x" + RoundUp(UserConfig.CustomNotificationSize.Height, 20)
default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20); };
}
}
} }
private static string NotificationTimer{ private static string NotificationTimer{
@@ -289,7 +285,7 @@ namespace TweetDuck.Core.Other.Analytics{
} }
string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account"; string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType; return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/" + accType : accType;
}catch{ }catch{
return "(unknown)"; return "(unknown)";
} }
@@ -310,7 +306,7 @@ namespace TweetDuck.Core.Other.Analytics{
} }
return new ExternalInfo{ return new ExternalInfo{
Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height, Resolution = screen.Bounds.Width + "x" + screen.Bounds.Height,
DPI = dpi DPI = dpi
}; };
} }

View File

@@ -12,7 +12,7 @@ namespace TweetDuck.Core.Other{
public FormAbout(){ public FormAbout(){
InitializeComponent(); InitializeComponent();
Text = "About "+Program.BrandName+" "+Program.VersionTag; Text = "About " + Program.BrandName + " " + Program.VersionTag;
labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license."; labelDescription.Text = "TweetDuck was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license.";

View File

@@ -7,8 +7,8 @@ using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetDuck.Core.Adapters;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Resources;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormGuide : Form, FormManager.IAppDialog{ sealed partial class FormGuide : Form, FormManager.IAppDialog{
@@ -37,7 +37,7 @@ namespace TweetDuck.Core.Other{
} }
public static void Show(string hash = null){ public static void Show(string hash = null){
string url = GuideUrl+(hash ?? string.Empty); string url = GuideUrl + (hash ?? string.Empty);
FormGuide guide = FormManager.TryFind<FormGuide>(); FormGuide guide = FormManager.TryFind<FormGuide>();
if (guide == null){ if (guide == null){
@@ -60,8 +60,8 @@ namespace TweetDuck.Core.Other{
private FormGuide(string url, FormBrowser owner){ private FormGuide(string url, FormBrowser owner){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Guide"; Text = Program.BrandName + " Guide";
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 resourceHandlerFactory = new ResourceHandlerFactory();
@@ -116,7 +116,7 @@ namespace TweetDuck.Core.Other{
} }
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"); CefScriptExecutor.RunScript(e.Frame, "Array.prototype.forEach.call(document.getElementsByTagName('A'), ele => ele.addEventListener('click', e => { e.preventDefault(); window.open(ele.getAttribute('href')); }))", "gen:links");
} }
} }
} }

View File

@@ -133,7 +133,7 @@ namespace TweetDuck.Core.Other{
Font = SystemFonts.MessageBoxFont, Font = SystemFonts.MessageBoxFont,
Location = new Point(0, 12), Location = new Point(0, 12),
Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)), Size = new Size(BrowserUtils.Scale(88, dpiScale), BrowserUtils.Scale(26, dpiScale)),
TabIndex = 256-buttonCount, TabIndex = 256 - buttonCount,
Text = title, Text = title,
UseVisualStyleBackColor = true UseVisualStyleBackColor = true
}; };
@@ -171,17 +171,17 @@ namespace TweetDuck.Core.Other{
control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale)); control.Size = new Size(BrowserUtils.Scale(control.Width, dpiScale), BrowserUtils.Scale(control.Height, dpiScale));
minFormWidth += control.Width+control.Margin.Horizontal; minFormWidth += control.Width + control.Margin.Horizontal;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
} }
private void RecalculateButtonLocation(){ private void RecalculateButtonLocation(){
int dist = ButtonDistance; int dist = ButtonDistance;
int start = ClientWidth-dist; int start = ClientWidth - dist;
for(int index = 0; index < buttonCount; index++){ for(int index = 0; index < buttonCount; index++){
Control control = panelActions.Controls[index]; Control control = panelActions.Controls[index];
control.Location = new Point(start-index*dist, control.Location.Y); control.Location = new Point(start - index * dist, control.Location.Y);
} }
} }
@@ -194,17 +194,17 @@ namespace TweetDuck.Core.Other{
int labelOffset = BrowserUtils.Scale(8, dpiScale); int labelOffset = BrowserUtils.Scale(8, dpiScale);
if (isMultiline && !wasLabelMultiline){ if (isMultiline && !wasLabelMultiline){
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y-labelOffset); labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y - labelOffset);
prevLabelHeight += labelOffset; prevLabelHeight += labelOffset;
} }
else if (!isMultiline && wasLabelMultiline){ else if (!isMultiline && wasLabelMultiline){
labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y+labelOffset); labelMessage.Location = new Point(labelMessage.Location.X, labelMessage.Location.Y + labelOffset);
prevLabelHeight -= labelOffset; prevLabelHeight -= labelOffset;
} }
realFormWidth = ClientWidth-(icon == null ? BrowserUtils.Scale(50, dpiScale) : 0)+labelMessage.Width-prevLabelWidth; realFormWidth = ClientWidth - (icon == null ? BrowserUtils.Scale(50, dpiScale) : 0) + labelMessage.Width - prevLabelWidth;
ClientWidth = Math.Max(realFormWidth, minFormWidth); ClientWidth = Math.Max(realFormWidth, minFormWidth);
Height += labelMessage.Height-prevLabelHeight; Height += labelMessage.Height - prevLabelHeight;
prevLabelWidth = labelMessage.Width; prevLabelWidth = labelMessage.Width;
prevLabelHeight = labelMessage.Height; prevLabelHeight = labelMessage.Height;
@@ -213,7 +213,7 @@ namespace TweetDuck.Core.Other{
protected override void OnPaint(PaintEventArgs e){ protected override void OnPaint(PaintEventArgs e){
if (icon != null){ if (icon != null){
e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1+BrowserUtils.Scale(25, dpiScale)); e.Graphics.DrawIcon(icon, BrowserUtils.Scale(25, dpiScale), 1 + BrowserUtils.Scale(25, dpiScale));
} }
base.OnPaint(e); base.OnPaint(e);

View File

@@ -1,4 +1,6 @@
namespace TweetDuck.Core.Other { using TweetDuck.Core.Controls;
namespace TweetDuck.Core.Other {
partial class FormPlugins { partial class FormPlugins {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
@@ -27,7 +29,7 @@
this.btnClose = new System.Windows.Forms.Button(); this.btnClose = new System.Windows.Forms.Button();
this.btnReload = new System.Windows.Forms.Button(); this.btnReload = new System.Windows.Forms.Button();
this.btnOpenFolder = new System.Windows.Forms.Button(); this.btnOpenFolder = new System.Windows.Forms.Button();
this.flowLayoutPlugins = new TweetDuck.Plugins.Controls.PluginListFlowLayout(); this.flowLayoutPlugins = new FlowLayoutPanelNoHScroll();
this.timerLayout = new System.Windows.Forms.Timer(this.components); this.timerLayout = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout(); this.SuspendLayout();
// //
@@ -117,7 +119,7 @@
private System.Windows.Forms.Button btnClose; private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnReload; private System.Windows.Forms.Button btnReload;
private System.Windows.Forms.Button btnOpenFolder; private System.Windows.Forms.Button btnOpenFolder;
private Plugins.Controls.PluginListFlowLayout flowLayoutPlugins; private FlowLayoutPanelNoHScroll flowLayoutPlugins;
private System.Windows.Forms.Timer timerLayout; private System.Windows.Forms.Timer timerLayout;
} }
} }

View File

@@ -1,11 +1,11 @@
using System; using System;
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.Configuration;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Controls; using TweetLib.Core;
using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormPlugins : Form, FormManager.IAppDialog{ sealed partial class FormPlugins : Form, FormManager.IAppDialog{
@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Other{
public FormPlugins(){ public FormPlugins(){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Plugins"; Text = Program.BrandName + " Plugins";
} }
public FormPlugins(PluginManager pluginManager) : this(){ public FormPlugins(PluginManager pluginManager) : this(){
@@ -69,8 +69,8 @@ namespace TweetDuck.Core.Other{
timerLayout.Stop(); timerLayout.Stop();
// stupid WinForms scrollbars and panels // stupid WinForms scrollbars and panels
Padding = new Padding(Padding.Left, Padding.Top, Padding.Right+1, Padding.Bottom+1); Padding = new Padding(Padding.Left, Padding.Top, Padding.Right + 1, Padding.Bottom + 1);
Padding = new Padding(Padding.Left, Padding.Top, Padding.Right-1, Padding.Bottom-1); Padding = new Padding(Padding.Left, Padding.Top, Padding.Right - 1, Padding.Bottom - 1);
} }
public void flowLayoutPlugins_Resize(object sender, EventArgs e){ public void flowLayoutPlugins_Resize(object sender, EventArgs e){
@@ -80,22 +80,22 @@ namespace TweetDuck.Core.Other{
return; return;
} }
bool showScrollBar = lastPlugin.Location.Y+lastPlugin.Height+1 >= flowLayoutPlugins.Height; bool showScrollBar = lastPlugin.Location.Y + lastPlugin.Height + 1 >= flowLayoutPlugins.Height;
int horizontalOffset = showScrollBar ? SystemInformation.VerticalScrollBarWidth : 0; int horizontalOffset = showScrollBar ? SystemInformation.VerticalScrollBarWidth : 0;
flowLayoutPlugins.AutoScroll = showScrollBar; flowLayoutPlugins.AutoScroll = showScrollBar;
flowLayoutPlugins.VerticalScroll.Visible = showScrollBar; flowLayoutPlugins.VerticalScroll.Visible = showScrollBar;
foreach(Control control in flowLayoutPlugins.Controls){ foreach(Control control in flowLayoutPlugins.Controls){
control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset; control.Width = flowLayoutPlugins.Width - control.Margin.Horizontal - horizontalOffset;
} }
flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count-1].Visible = !showScrollBar; flowLayoutPlugins.Controls[flowLayoutPlugins.Controls.Count - 1].Visible = !showScrollBar;
flowLayoutPlugins.Focus(); flowLayoutPlugins.Focus();
} }
private void btnOpenFolder_Click(object sender, EventArgs e){ private void btnOpenFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", '"'+pluginManager.PathCustomPlugins+'"')){} App.SystemHandler.OpenFileExplorer(pluginManager.PathCustomPlugins);
} }
private void btnReload_Click(object sender, EventArgs e){ private void btnReload_Click(object sender, EventArgs e){

View File

@@ -9,8 +9,8 @@ using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Settings; using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetLib.Core.Features.Plugins;
using TweetDuck.Updates; using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormSettings : Form, FormManager.IAppDialog{ sealed partial class FormSettings : Form, FormManager.IAppDialog{
@@ -27,7 +27,7 @@ namespace TweetDuck.Core.Other{
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){ public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options"; Text = Program.BrandName + " Options";
this.browser = browser; this.browser = browser;
this.browser.PauseNotification(); this.browser.PauseNotification();
@@ -41,6 +41,7 @@ namespace TweetDuck.Core.Other{
AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates)); AddButton("General", () => new TabSettingsGeneral(this.browser.ReloadColumns, updates));
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins))); AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification)); AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification));
AddButton("Tray", () => new TabSettingsTray());
AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins)); AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools)); AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS, this.browser.OpenDevTools));
@@ -110,7 +111,7 @@ namespace TweetDuck.Core.Other{
BackColor = SystemColors.Control, BackColor = SystemColors.Control,
FlatStyle = FlatStyle.Flat, FlatStyle = FlatStyle.Flat,
Font = SystemFonts.MessageBoxFont, Font = SystemFonts.MessageBoxFont,
Location = new Point(0, (buttonHeight+1)*(panelButtons.Controls.Count/2)), Location = new Point(0, (buttonHeight + 1) * (panelButtons.Controls.Count / 2)),
Margin = new Padding(0), Margin = new Padding(0),
Size = new Size(panelButtons.Width, buttonHeight), Size = new Size(panelButtons.Width, buttonHeight),
Text = title, Text = title,
@@ -125,7 +126,7 @@ namespace TweetDuck.Core.Other{
panelButtons.Controls.Add(new Panel{ panelButtons.Controls.Add(new Panel{
BackColor = Color.DimGray, BackColor = Color.DimGray,
Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count-1].Location.Y+buttonHeight), Location = new Point(0, panelButtons.Controls[panelButtons.Controls.Count - 1].Location.Y + buttonHeight),
Margin = new Padding(0), Margin = new Padding(0),
Size = new Size(panelButtons.Width, 1) Size = new Size(panelButtons.Width, 1)
}); });
@@ -157,8 +158,8 @@ namespace TweetDuck.Core.Other{
} }
} }
if (tab.Control.Height < panelContents.Height-2){ if (tab.Control.Height < panelContents.Height - 2){
tab.Control.Height = panelContents.Height-2; // fixes off-by-pixel error on high DPI tab.Control.Height = panelContents.Height - 2; // fixes off-by-pixel error on high DPI
} }
tab.Control.OnReady(); tab.Control.OnReady();
@@ -195,7 +196,7 @@ namespace TweetDuck.Core.Other{
private sealed class SettingsTab{ private sealed class SettingsTab{
public Button Button { get; } public Button Button { get; }
public BaseTabSettings Control => control ?? (control = constructor()); public BaseTabSettings Control => control ??= constructor();
public bool IsInitialized => control != null; public bool IsInitialized => control != null;
private readonly Func<BaseTabSettings> constructor; private readonly Func<BaseTabSettings> constructor;

View File

@@ -9,7 +9,7 @@ namespace TweetDuck.Core.Other.Settings{
public IEnumerable<Control> InteractiveControls{ public IEnumerable<Control> InteractiveControls{
get{ get{
IEnumerable<Control> FindInteractiveControls(Control parent){ static IEnumerable<Control> FindInteractiveControls(Control parent){
foreach(Control control in parent.Controls){ foreach(Control control in parent.Controls){
if (control is Panel subPanel){ if (control is Panel subPanel){
foreach(Control subControl in FindInteractiveControls(subPanel)){ foreach(Control subControl in FindInteractiveControls(subPanel)){

View File

@@ -5,12 +5,10 @@ using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsAnalytics : Form{ sealed partial class DialogSettingsAnalytics : Form{
public string CefArgs => textBoxReport.Text;
public DialogSettingsAnalytics(AnalyticsReport report){ public DialogSettingsAnalytics(AnalyticsReport report){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options - Analytics Report"; Text = Program.BrandName + " Options - Analytics Report";
textBoxReport.EnableMultilineShortcuts(); textBoxReport.EnableMultilineShortcuts();
textBoxReport.Text = report.ToString().TrimEnd(); textBoxReport.Text = report.ToString().TrimEnd();

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCSS(string browserCSS, string notificationCSS, 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";
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
this.openDevTools = openDevTools; this.openDevTools = openDevTools;
@@ -64,21 +64,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
} }
if (!(deleteTo < text.Length-1 && text[deleteTo] == '\r' && text[deleteTo+1] == '\n')){ if (!(deleteTo < text.Length - 1 && text[deleteTo] == '\r' && text[deleteTo + 1] == '\n')){
++deleteTo; ++deleteTo;
} }
tb.Select(deleteTo, tb.SelectionLength+tb.SelectionStart-deleteTo); tb.Select(deleteTo, tb.SelectionLength + tb.SelectionStart - deleteTo);
tb.SelectedText = string.Empty; tb.SelectedText = string.Empty;
} }
} }
else if (e.KeyCode == Keys.Back && e.Modifiers == Keys.None){ else if (e.KeyCode == Keys.Back && e.Modifiers == Keys.None){
int deleteTo = tb.SelectionStart; int deleteTo = tb.SelectionStart;
if (deleteTo > 1 && text[deleteTo-1] == ' ' && text[deleteTo-2] == ' '){ if (deleteTo > 1 && text[deleteTo - 1] == ' ' && text[deleteTo - 2] == ' '){
e.SuppressKeyPress = true; e.SuppressKeyPress = true;
tb.Select(deleteTo-2, 2); tb.Select(deleteTo - 2, 2);
tb.SelectedText = string.Empty; tb.SelectedText = string.Empty;
} }
} }
@@ -89,28 +89,28 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
if (insertAt == 0){ if (insertAt == 0){
return; return;
} }
else if (text[insertAt-1] == '{'){ else if (text[insertAt - 1] == '{'){
insertText = Environment.NewLine+" "; insertText = Environment.NewLine + " ";
int nextBracket = insertAt < text.Length ? text.IndexOfAny(new char[]{ '{', '}' }, insertAt+1) : -1; int nextBracket = insertAt < text.Length ? text.IndexOfAny(new char[]{ '{', '}' }, insertAt + 1) : -1;
if (nextBracket == -1 || text[nextBracket] == '{'){ if (nextBracket == -1 || text[nextBracket] == '{'){
string insertExtra = Environment.NewLine+"}"; string insertExtra = Environment.NewLine + "}";
insertText += insertExtra; insertText += insertExtra;
cursorOffset -= insertExtra.Length; cursorOffset -= insertExtra.Length;
} }
} }
else{ else{
int lineStart = text.LastIndexOf('\n', tb.SelectionStart-1); int lineStart = text.LastIndexOf('\n', tb.SelectionStart - 1);
Match match = Regex.Match(text.Substring(lineStart == -1 ? 0 : lineStart+1), "^([ \t]+)"); Match match = Regex.Match(text.Substring(lineStart == -1 ? 0 : lineStart + 1), "^([ \t]+)");
insertText = match.Success ? Environment.NewLine+match.Groups[1].Value : null; insertText = match.Success ? Environment.NewLine + match.Groups[1].Value : null;
} }
if (!string.IsNullOrEmpty(insertText)){ if (!string.IsNullOrEmpty(insertText)){
e.SuppressKeyPress = true; e.SuppressKeyPress = true;
tb.Text = text.Insert(insertAt, insertText); tb.Text = text.Insert(insertAt, insertText);
tb.SelectionStart = insertAt+cursorOffset+insertText.Length; tb.SelectionStart = insertAt + cursorOffset + insertText.Length;
} }
} }
} }

View File

@@ -2,7 +2,7 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetLib.Core.Collections;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsCefArgs : Form{ sealed partial class DialogSettingsCefArgs : Form{
@@ -13,7 +13,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsCefArgs(string args){ public DialogSettingsCefArgs(string args){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options - CEF Arguments"; Text = Program.BrandName + " Options - CEF Arguments";
textBoxArgs.EnableMultilineShortcuts(); textBoxArgs.EnableMultilineShortcuts();
textBoxArgs.Text = initialArgs = args ?? ""; textBoxArgs.Text = initialArgs = args ?? "";
@@ -32,7 +32,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
int count = CommandLineArgs.ReadCefArguments(CefArgs).Count; int count = CommandLineArgs.ReadCefArguments(CefArgs).Count;
string prompt = count == 0 && !string.IsNullOrWhiteSpace(initialArgs) ? "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;

View File

@@ -0,0 +1,154 @@
namespace TweetDuck.Core.Other.Settings.Dialogs {
partial class DialogSettingsExternalProgram {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.textBoxPath = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnApply = new System.Windows.Forms.Button();
this.labelPath = new System.Windows.Forms.Label();
this.labelArgs = new System.Windows.Forms.Label();
this.textBoxArgs = new System.Windows.Forms.TextBox();
this.btnBrowse = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// textBoxPath
//
this.textBoxPath.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxPath.Font = new System.Drawing.Font("Segoe UI", 9F);
this.textBoxPath.Location = new System.Drawing.Point(12, 30);
this.textBoxPath.Name = "textBoxPath";
this.textBoxPath.Size = new System.Drawing.Size(336, 23);
this.textBoxPath.TabIndex = 1;
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true;
this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnCancel.Location = new System.Drawing.Point(307, 118);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCancel.Size = new System.Drawing.Size(57, 25);
this.btnCancel.TabIndex = 6;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// btnApply
//
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnApply.AutoSize = true;
this.btnApply.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnApply.Location = new System.Drawing.Point(370, 118);
this.btnApply.Name = "btnApply";
this.btnApply.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnApply.Size = new System.Drawing.Size(52, 25);
this.btnApply.TabIndex = 5;
this.btnApply.Text = "Apply";
this.btnApply.UseVisualStyleBackColor = true;
this.btnApply.Click += new System.EventHandler(this.btnApply_Click);
//
// labelPath
//
this.labelPath.AutoSize = true;
this.labelPath.Font = new System.Drawing.Font("Segoe UI", 9F);
this.labelPath.Location = new System.Drawing.Point(12, 9);
this.labelPath.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.labelPath.Name = "labelPath";
this.labelPath.Size = new System.Drawing.Size(109, 15);
this.labelPath.TabIndex = 0;
this.labelPath.Text = "Path to Application";
//
// labelArgs
//
this.labelArgs.AutoSize = true;
this.labelArgs.Font = new System.Drawing.Font("Segoe UI", 9F);
this.labelArgs.Location = new System.Drawing.Point(12, 68);
this.labelArgs.Margin = new System.Windows.Forms.Padding(3, 12, 3, 3);
this.labelArgs.Name = "labelArgs";
this.labelArgs.Size = new System.Drawing.Size(124, 15);
this.labelArgs.TabIndex = 3;
this.labelArgs.Text = "Additional Arguments";
//
// textBoxArgs
//
this.textBoxArgs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxArgs.Font = new System.Drawing.Font("Segoe UI", 9F);
this.textBoxArgs.Location = new System.Drawing.Point(12, 89);
this.textBoxArgs.Name = "textBoxArgs";
this.textBoxArgs.Size = new System.Drawing.Size(410, 23);
this.textBoxArgs.TabIndex = 4;
//
// btnBrowse
//
this.btnBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnBrowse.AutoSize = true;
this.btnBrowse.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnBrowse.Location = new System.Drawing.Point(354, 28);
this.btnBrowse.Name = "btnBrowse";
this.btnBrowse.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnBrowse.Size = new System.Drawing.Size(68, 25);
this.btnBrowse.TabIndex = 2;
this.btnBrowse.Text = "Browse...";
this.btnBrowse.UseVisualStyleBackColor = true;
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
//
// DialogSettingsExternalProgram
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(434, 155);
this.Controls.Add(this.btnBrowse);
this.Controls.Add(this.textBoxArgs);
this.Controls.Add(this.labelArgs);
this.Controls.Add(this.labelPath);
this.Controls.Add(this.btnApply);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.textBoxPath);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "DialogSettingsExternalProgram";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBoxPath;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnApply;
private System.Windows.Forms.Label labelPath;
private System.Windows.Forms.Label labelArgs;
private System.Windows.Forms.TextBox textBoxArgs;
private System.Windows.Forms.Button btnBrowse;
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Windows.Forms;
using TweetLib.Core.Utils;
using IOPath = System.IO.Path;
namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsExternalProgram : Form{
public string Path{
get => StringUtils.NullIfEmpty(textBoxPath.Text);
set => textBoxPath.Text = value ?? string.Empty;
}
public string Args{
get => StringUtils.NullIfEmpty(textBoxArgs.Text);
set => textBoxArgs.Text = value ?? string.Empty;
}
private readonly string fileDialogTitle;
public DialogSettingsExternalProgram(string windowTitle, string fileDialogTitle){
InitializeComponent();
Text = Program.BrandName + " Options - " + windowTitle;
AcceptButton = btnApply;
CancelButton = btnCancel;
this.fileDialogTitle = fileDialogTitle;
}
private void btnBrowse_Click(object sender, EventArgs e){
using OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
InitialDirectory = IOPath.GetDirectoryName(Path), // returns null if argument is null
Title = fileDialogTitle,
Filter = "Executables (*.exe;*.bat;*.cmd)|*.exe;*.bat;*.cmd|All Files (*.*)|*.*"
};
if (dialog.ShowDialog() == DialogResult.OK && Path != dialog.FileName){
Path = dialog.FileName;
Args = string.Empty;
}
}
private void btnApply_Click(object sender, EventArgs e){
DialogResult = DialogResult.OK;
Close();
}
private void btnCancel_Click(object sender, EventArgs e){
DialogResult = DialogResult.Cancel;
Close();
}
}
}

View File

@@ -4,8 +4,8 @@ using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Utils; using TweetLib.Core.Features.Plugins;
using TweetDuck.Plugins; using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsManage : Form{ sealed partial class DialogSettingsManage : Form{
@@ -124,7 +124,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
// Continue... // Continue...
panelDecision.Visible = false; panelDecision.Visible = false;
panelSelection.Visible = true; panelSelection.Visible = true;
Height += panelSelection.Height-panelDecision.Height; Height += panelSelection.Height - panelDecision.Height;
break; break;
case State.Reset: case State.Reset:

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Data; using TweetLib.Core.Collections;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsRestart : Form{ sealed partial class DialogSettingsRestart : Form{
@@ -18,13 +18,13 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
tbDataFolder.Enabled = false; tbDataFolder.Enabled = false;
} }
else{ else{
tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder, string.Empty); tbDataFolder.Text = currentArgs.GetValue(Arguments.ArgDataFolder) ?? string.Empty;
tbDataFolder.TextChanged += control_Change; tbDataFolder.TextChanged += control_Change;
} }
control_Change(this, EventArgs.Empty); control_Change(this, EventArgs.Empty);
Text = Program.BrandName+" Arguments"; Text = Program.BrandName + " Arguments";
} }
private void control_Change(object sender, EventArgs e){ private void control_Change(object sender, EventArgs e){

View File

@@ -8,7 +8,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
public DialogSettingsSearchEngine(){ public DialogSettingsSearchEngine(){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options - Custom Search Engine"; Text = Program.BrandName + " Options - Custom Search Engine";
textBoxUrl.Text = Program.Config.User.SearchEngineUrl ?? ""; textBoxUrl.Text = Program.Config.User.SearchEngineUrl ?? "";
textBoxUrl.Select(textBoxUrl.Text.Length, 0); textBoxUrl.Select(textBoxUrl.Text.Length, 0);

View File

@@ -41,6 +41,8 @@
this.panelConfiguration = new System.Windows.Forms.Panel(); this.panelConfiguration = new System.Windows.Forms.Panel();
this.labelConfiguration = new System.Windows.Forms.Label(); this.labelConfiguration = new System.Windows.Forms.Label();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.labelDevTools = new System.Windows.Forms.Label();
this.checkDevToolsWindowOnTop = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit();
this.panelAppButtons.SuspendLayout(); this.panelAppButtons.SuspendLayout();
this.panelClearCacheAuto.SuspendLayout(); this.panelClearCacheAuto.SuspendLayout();
@@ -240,6 +242,8 @@
this.flowPanel.Controls.Add(this.panelClearCacheAuto); this.flowPanel.Controls.Add(this.panelClearCacheAuto);
this.flowPanel.Controls.Add(this.labelConfiguration); this.flowPanel.Controls.Add(this.labelConfiguration);
this.flowPanel.Controls.Add(this.panelConfiguration); this.flowPanel.Controls.Add(this.panelConfiguration);
this.flowPanel.Controls.Add(this.labelDevTools);
this.flowPanel.Controls.Add(this.checkDevToolsWindowOnTop);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9); this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel"; this.flowPanel.Name = "flowPanel";
@@ -247,6 +251,29 @@
this.flowPanel.TabIndex = 0; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// labelDevTools
//
this.labelDevTools.AutoSize = true;
this.labelDevTools.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelDevTools.Location = new System.Drawing.Point(0, 302);
this.labelDevTools.Margin = new System.Windows.Forms.Padding(0, 30, 0, 1);
this.labelDevTools.Name = "labelDevTools";
this.labelDevTools.Size = new System.Drawing.Size(156, 19);
this.labelDevTools.TabIndex = 7;
this.labelDevTools.Text = "DEVELOPMENT TOOLS";
//
// checkDevToolsWindowOnTop
//
this.checkDevToolsWindowOnTop.AutoSize = true;
this.checkDevToolsWindowOnTop.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkDevToolsWindowOnTop.Location = new System.Drawing.Point(6, 328);
this.checkDevToolsWindowOnTop.Margin = new System.Windows.Forms.Padding(6, 6, 0, 2);
this.checkDevToolsWindowOnTop.Name = "checkDevToolsWindowOnTop";
this.checkDevToolsWindowOnTop.Size = new System.Drawing.Size(168, 19);
this.checkDevToolsWindowOnTop.TabIndex = 8;
this.checkDevToolsWindowOnTop.Text = "Dev Tools Window On Top";
this.checkDevToolsWindowOnTop.UseVisualStyleBackColor = true;
//
// TabSettingsAdvanced // TabSettingsAdvanced
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -284,5 +311,7 @@
private Controls.NumericUpDownEx numClearCacheThreshold; private Controls.NumericUpDownEx numClearCacheThreshold;
private System.Windows.Forms.CheckBox checkClearCacheAuto; private System.Windows.Forms.CheckBox checkClearCacheAuto;
private System.Windows.Forms.FlowLayoutPanel flowPanel; private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.Label labelDevTools;
private System.Windows.Forms.CheckBox checkDevToolsWindowOnTop;
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
@@ -7,6 +6,7 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Core;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsAdvanced : BaseTabSettings{ sealed partial class TabSettingsAdvanced : BaseTabSettings{
@@ -36,7 +36,7 @@ namespace TweetDuck.Core.Other.Settings{
numClearCacheThreshold.SetValueSafe(SysConfig.ClearCacheThreshold); numClearCacheThreshold.SetValueSafe(SysConfig.ClearCacheThreshold);
BrowserCache.GetCacheSize(task => { BrowserCache.GetCacheSize(task => {
string text = task.Status == TaskStatus.RanToCompletion ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "unknown"; string text = task.Status == TaskStatus.RanToCompletion ? (int)Math.Ceiling(task.Result / (1024.0 * 1024.0)) + " MB" : "unknown";
this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})"); this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})");
}); });
@@ -44,6 +44,16 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework."); toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows."); toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows.");
// development tools
toolTip.SetToolTip(checkDevToolsWindowOnTop, "Sets whether dev tool windows appears on top of other windows.");
checkDevToolsWindowOnTop.Checked = Config.DevToolsWindowOnTop;
if (!BrowserUtils.HasDevTools){
checkDevToolsWindowOnTop.Enabled = false;
}
} }
public override void OnReady(){ public override void OnReady(){
@@ -57,6 +67,8 @@ namespace TweetDuck.Core.Other.Settings{
btnEditCefArgs.Click += btnEditCefArgs_Click; btnEditCefArgs.Click += btnEditCefArgs_Click;
btnEditCSS.Click += btnEditCSS_Click; btnEditCSS.Click += btnEditCSS_Click;
checkDevToolsWindowOnTop.CheckedChanged += checkDevToolsWindowOnTop_CheckedChanged;
} }
public override void OnClosing(){ public override void OnClosing(){
@@ -67,11 +79,11 @@ namespace TweetDuck.Core.Other.Settings{
#region Application #region Application
private void btnOpenAppFolder_Click(object sender, EventArgs e){ private void btnOpenAppFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){} App.SystemHandler.OpenFileExplorer(Program.ProgramPath);
} }
private void btnOpenDataFolder_Click(object sender, EventArgs e){ private void btnOpenDataFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", "\""+Program.StoragePath+"\"")){} App.SystemHandler.OpenFileExplorer(Program.StoragePath);
} }
private void btnRestart_Click(object sender, EventArgs e){ private void btnRestart_Click(object sender, EventArgs e){
@@ -152,6 +164,13 @@ namespace TweetDuck.Core.Other.Settings{
} }
} }
#endregion
#region Development Tools
private void checkDevToolsWindowOnTop_CheckedChanged(object sender, EventArgs e){
Config.DevToolsWindowOnTop = checkDevToolsWindowOnTop.Checked;
}
#endregion #endregion
} }
} }

View File

@@ -3,7 +3,7 @@ using System.Windows.Forms;
using TweetDuck.Core.Other.Analytics; 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 TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsFeedback : BaseTabSettings{ sealed partial class TabSettingsFeedback : BaseTabSettings{
@@ -24,7 +24,7 @@ namespace TweetDuck.Core.Other.Settings{
if (analytics != null){ if (analytics != null){
string collectionTime = analyticsFile.LastCollectionMessage; string collectionTime = analyticsFile.LastCollectionMessage;
labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: "+collectionTime; labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: " + collectionTime;
} }
} }

View File

@@ -41,30 +41,37 @@
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel(); this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
this.checkFocusDmInput = new System.Windows.Forms.CheckBox(); 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.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayIcon = new System.Windows.Forms.Label();
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.labelBrowserSettings = new System.Windows.Forms.Label(); this.labelBrowserSettings = new System.Windows.Forms.Label();
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox(); this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
this.checkTouchAdjustment = new System.Windows.Forms.CheckBox(); this.checkTouchAdjustment = new System.Windows.Forms.CheckBox();
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
this.labelBrowserPath = new System.Windows.Forms.Label(); this.labelBrowserPath = new System.Windows.Forms.Label();
this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox(); this.comboBoxCustomBrowser = new System.Windows.Forms.ComboBox();
this.labelSearchEngine = new System.Windows.Forms.Label(); this.labelSearchEngine = new System.Windows.Forms.Label();
this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox(); this.comboBoxSearchEngine = new System.Windows.Forms.ComboBox();
this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel(); this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel();
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
this.labelLocales = new System.Windows.Forms.Label(); this.labelLocales = new System.Windows.Forms.Label();
this.checkSpellCheck = new System.Windows.Forms.CheckBox(); this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.labelSpellCheckLanguage = new System.Windows.Forms.Label(); this.labelSpellCheckLanguage = new System.Windows.Forms.Label();
this.comboBoxSpellCheckLanguage = new System.Windows.Forms.ComboBox(); this.comboBoxSpellCheckLanguage = new System.Windows.Forms.ComboBox();
this.labelTranslationTarget = new System.Windows.Forms.Label(); this.labelTranslationTarget = new System.Windows.Forms.Label();
this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox(); this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox();
this.labelFirstDayOfWeek = new System.Windows.Forms.Label();
this.comboBoxFirstDayOfWeek = new System.Windows.Forms.ComboBox();
this.labelExternalApplications = new System.Windows.Forms.Label();
this.panelCustomBrowser = new System.Windows.Forms.Panel();
this.btnCustomBrowserChange = new System.Windows.Forms.Button();
this.labelVideoPlayerPath = new System.Windows.Forms.Label();
this.panelCustomVideoPlayer = new System.Windows.Forms.Panel();
this.comboBoxCustomVideoPlayer = new System.Windows.Forms.ComboBox();
this.btnCustomVideoPlayerChange = new System.Windows.Forms.Button();
this.panelSeparator = new System.Windows.Forms.Panel(); this.panelSeparator = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.panelZoom.SuspendLayout(); this.panelZoom.SuspendLayout();
this.flowPanelLeft.SuspendLayout(); this.flowPanelLeft.SuspendLayout();
this.flowPanelRight.SuspendLayout(); this.flowPanelRight.SuspendLayout();
this.panelCustomBrowser.SuspendLayout();
this.panelCustomVideoPlayer.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// checkExpandLinks // checkExpandLinks
@@ -83,7 +90,7 @@
// //
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, 409); this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 403);
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);
@@ -95,7 +102,7 @@
// //
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, 433); this.btnCheckUpdates.Location = new System.Drawing.Point(5, 427);
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);
@@ -159,11 +166,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, 179); this.labelZoom.Location = new System.Drawing.Point(3, 299);
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 = 7; this.labelZoom.TabIndex = 11;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// zoomUpdateTimer // zoomUpdateTimer
@@ -186,11 +193,11 @@
// //
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, 195); this.panelZoom.Location = new System.Drawing.Point(0, 315);
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 = 8; this.panelZoom.TabIndex = 12;
// //
// checkAnimatedAvatars // checkAnimatedAvatars
// //
@@ -208,7 +215,7 @@
// //
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, 383); this.labelUpdates.Location = new System.Drawing.Point(0, 377);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 27, 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);
@@ -226,12 +233,12 @@
this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen); this.flowPanelLeft.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
this.flowPanelLeft.Controls.Add(this.checkBestImageQuality); this.flowPanelLeft.Controls.Add(this.checkBestImageQuality);
this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars); this.flowPanelLeft.Controls.Add(this.checkAnimatedAvatars);
this.flowPanelLeft.Controls.Add(this.labelBrowserSettings);
this.flowPanelLeft.Controls.Add(this.checkSmoothScrolling);
this.flowPanelLeft.Controls.Add(this.checkTouchAdjustment);
this.flowPanelLeft.Controls.Add(this.checkHardwareAcceleration);
this.flowPanelLeft.Controls.Add(this.labelZoom); this.flowPanelLeft.Controls.Add(this.labelZoom);
this.flowPanelLeft.Controls.Add(this.panelZoom); this.flowPanelLeft.Controls.Add(this.panelZoom);
this.flowPanelLeft.Controls.Add(this.labelTray);
this.flowPanelLeft.Controls.Add(this.comboBoxTrayType);
this.flowPanelLeft.Controls.Add(this.labelTrayIcon);
this.flowPanelLeft.Controls.Add(this.checkTrayHighlight);
this.flowPanelLeft.Controls.Add(this.labelUpdates); this.flowPanelLeft.Controls.Add(this.labelUpdates);
this.flowPanelLeft.Controls.Add(this.checkUpdateNotifications); this.flowPanelLeft.Controls.Add(this.checkUpdateNotifications);
this.flowPanelLeft.Controls.Add(this.btnCheckUpdates); this.flowPanelLeft.Controls.Add(this.btnCheckUpdates);
@@ -266,71 +273,26 @@
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open"; this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true; this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
// //
// labelTray
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelTray.Location = new System.Drawing.Point(0, 255);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(99, 19);
this.labelTray.TabIndex = 9;
this.labelTray.Text = "SYSTEM TRAY";
//
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 279);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
this.comboBoxTrayType.TabIndex = 10;
//
// labelTrayIcon
//
this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTrayIcon.Location = new System.Drawing.Point(3, 314);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
this.labelTrayIcon.TabIndex = 11;
this.labelTrayIcon.Text = "Tray Icon";
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 335);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
this.checkTrayHighlight.TabIndex = 12;
this.checkTrayHighlight.Text = "Enable Highlight";
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// labelBrowserSettings // labelBrowserSettings
// //
this.labelBrowserSettings.AutoSize = true; this.labelBrowserSettings.AutoSize = true;
this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); this.labelBrowserSettings.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 0); this.labelBrowserSettings.Location = new System.Drawing.Point(0, 192);
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1); this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
this.labelBrowserSettings.Name = "labelBrowserSettings"; this.labelBrowserSettings.Name = "labelBrowserSettings";
this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19); this.labelBrowserSettings.Size = new System.Drawing.Size(143, 19);
this.labelBrowserSettings.TabIndex = 0; this.labelBrowserSettings.TabIndex = 7;
this.labelBrowserSettings.Text = "BROWSER SETTINGS"; this.labelBrowserSettings.Text = "BROWSER SETTINGS";
// //
// checkSmoothScrolling // checkSmoothScrolling
// //
this.checkSmoothScrolling.AutoSize = true; this.checkSmoothScrolling.AutoSize = true;
this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F); this.checkSmoothScrolling.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 26); this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 218);
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkSmoothScrolling.Name = "checkSmoothScrolling"; this.checkSmoothScrolling.Name = "checkSmoothScrolling";
this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19); this.checkSmoothScrolling.Size = new System.Drawing.Size(117, 19);
this.checkSmoothScrolling.TabIndex = 1; this.checkSmoothScrolling.TabIndex = 8;
this.checkSmoothScrolling.Text = "Smooth Scrolling"; this.checkSmoothScrolling.Text = "Smooth Scrolling";
this.checkSmoothScrolling.UseVisualStyleBackColor = true; this.checkSmoothScrolling.UseVisualStyleBackColor = true;
// //
@@ -338,45 +300,57 @@
// //
this.checkTouchAdjustment.AutoSize = true; this.checkTouchAdjustment.AutoSize = true;
this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F); this.checkTouchAdjustment.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 50); this.checkTouchAdjustment.Location = new System.Drawing.Point(6, 242);
this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2); this.checkTouchAdjustment.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkTouchAdjustment.Name = "checkTouchAdjustment"; this.checkTouchAdjustment.Name = "checkTouchAdjustment";
this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19); this.checkTouchAdjustment.Size = new System.Drawing.Size(163, 19);
this.checkTouchAdjustment.TabIndex = 2; this.checkTouchAdjustment.TabIndex = 9;
this.checkTouchAdjustment.Text = "Touch Screen Adjustment"; this.checkTouchAdjustment.Text = "Touch Screen Adjustment";
this.checkTouchAdjustment.UseVisualStyleBackColor = true; this.checkTouchAdjustment.UseVisualStyleBackColor = true;
// //
// checkHardwareAcceleration
//
this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 266);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19);
this.checkHardwareAcceleration.TabIndex = 10;
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
//
// labelBrowserPath // labelBrowserPath
// //
this.labelBrowserPath.AutoSize = true; this.labelBrowserPath.AutoSize = true;
this.labelBrowserPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); this.labelBrowserPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelBrowserPath.Location = new System.Drawing.Point(3, 107); this.labelBrowserPath.Location = new System.Drawing.Point(3, 275);
this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelBrowserPath.Name = "labelBrowserPath"; this.labelBrowserPath.Name = "labelBrowserPath";
this.labelBrowserPath.Size = new System.Drawing.Size(104, 15); this.labelBrowserPath.Size = new System.Drawing.Size(104, 15);
this.labelBrowserPath.TabIndex = 4; this.labelBrowserPath.TabIndex = 9;
this.labelBrowserPath.Text = "Open Links With..."; this.labelBrowserPath.Text = "Open Links With...";
// //
// comboBoxBrowserPath // comboBoxCustomBrowser
// //
this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxCustomBrowser.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxBrowserPath.Font = new System.Drawing.Font("Segoe UI", 9F); this.comboBoxCustomBrowser.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxBrowserPath.FormattingEnabled = true; this.comboBoxCustomBrowser.FormattingEnabled = true;
this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 126); this.comboBoxCustomBrowser.Location = new System.Drawing.Point(5, 1);
this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.comboBoxCustomBrowser.Margin = new System.Windows.Forms.Padding(5, 1, 3, 0);
this.comboBoxBrowserPath.Name = "comboBoxBrowserPath"; this.comboBoxCustomBrowser.Name = "comboBoxCustomBrowser";
this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 23); this.comboBoxCustomBrowser.Size = new System.Drawing.Size(173, 23);
this.comboBoxBrowserPath.TabIndex = 5; this.comboBoxCustomBrowser.TabIndex = 0;
// //
// labelSearchEngine // labelSearchEngine
// //
this.labelSearchEngine.AutoSize = true; this.labelSearchEngine.AutoSize = true;
this.labelSearchEngine.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); this.labelSearchEngine.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelSearchEngine.Location = new System.Drawing.Point(3, 164); this.labelSearchEngine.Location = new System.Drawing.Point(3, 389);
this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelSearchEngine.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelSearchEngine.Name = "labelSearchEngine"; this.labelSearchEngine.Name = "labelSearchEngine";
this.labelSearchEngine.Size = new System.Drawing.Size(82, 15); this.labelSearchEngine.Size = new System.Drawing.Size(82, 15);
this.labelSearchEngine.TabIndex = 6; this.labelSearchEngine.TabIndex = 13;
this.labelSearchEngine.Text = "Search Engine"; this.labelSearchEngine.Text = "Search Engine";
// //
// comboBoxSearchEngine // comboBoxSearchEngine
@@ -384,30 +358,31 @@
this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxSearchEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F); this.comboBoxSearchEngine.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxSearchEngine.FormattingEnabled = true; this.comboBoxSearchEngine.FormattingEnabled = true;
this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 183); this.comboBoxSearchEngine.Location = new System.Drawing.Point(5, 408);
this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.comboBoxSearchEngine.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxSearchEngine.Name = "comboBoxSearchEngine"; this.comboBoxSearchEngine.Name = "comboBoxSearchEngine";
this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 23); this.comboBoxSearchEngine.Size = new System.Drawing.Size(173, 23);
this.comboBoxSearchEngine.TabIndex = 7; this.comboBoxSearchEngine.TabIndex = 14;
// //
// flowPanelRight // flowPanelRight
// //
this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left))); | System.Windows.Forms.AnchorStyles.Left)));
this.flowPanelRight.Controls.Add(this.labelBrowserSettings);
this.flowPanelRight.Controls.Add(this.checkSmoothScrolling);
this.flowPanelRight.Controls.Add(this.checkTouchAdjustment);
this.flowPanelRight.Controls.Add(this.checkHardwareAcceleration);
this.flowPanelRight.Controls.Add(this.labelBrowserPath);
this.flowPanelRight.Controls.Add(this.comboBoxBrowserPath);
this.flowPanelRight.Controls.Add(this.labelSearchEngine);
this.flowPanelRight.Controls.Add(this.comboBoxSearchEngine);
this.flowPanelRight.Controls.Add(this.labelLocales); this.flowPanelRight.Controls.Add(this.labelLocales);
this.flowPanelRight.Controls.Add(this.checkSpellCheck); this.flowPanelRight.Controls.Add(this.checkSpellCheck);
this.flowPanelRight.Controls.Add(this.labelSpellCheckLanguage); this.flowPanelRight.Controls.Add(this.labelSpellCheckLanguage);
this.flowPanelRight.Controls.Add(this.comboBoxSpellCheckLanguage); this.flowPanelRight.Controls.Add(this.comboBoxSpellCheckLanguage);
this.flowPanelRight.Controls.Add(this.labelTranslationTarget); this.flowPanelRight.Controls.Add(this.labelTranslationTarget);
this.flowPanelRight.Controls.Add(this.comboBoxTranslationTarget); this.flowPanelRight.Controls.Add(this.comboBoxTranslationTarget);
this.flowPanelRight.Controls.Add(this.labelFirstDayOfWeek);
this.flowPanelRight.Controls.Add(this.comboBoxFirstDayOfWeek);
this.flowPanelRight.Controls.Add(this.labelExternalApplications);
this.flowPanelRight.Controls.Add(this.labelBrowserPath);
this.flowPanelRight.Controls.Add(this.panelCustomBrowser);
this.flowPanelRight.Controls.Add(this.labelVideoPlayerPath);
this.flowPanelRight.Controls.Add(this.panelCustomVideoPlayer);
this.flowPanelRight.Controls.Add(this.labelSearchEngine);
this.flowPanelRight.Controls.Add(this.comboBoxSearchEngine);
this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanelRight.Location = new System.Drawing.Point(322, 9); this.flowPanelRight.Location = new System.Drawing.Point(322, 9);
this.flowPanelRight.Name = "flowPanelRight"; this.flowPanelRight.Name = "flowPanelRight";
@@ -415,38 +390,26 @@
this.flowPanelRight.TabIndex = 1; this.flowPanelRight.TabIndex = 1;
this.flowPanelRight.WrapContents = false; this.flowPanelRight.WrapContents = false;
// //
// checkHardwareAcceleration
//
this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 74);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 3, 3, 2);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(146, 19);
this.checkHardwareAcceleration.TabIndex = 3;
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
//
// labelLocales // labelLocales
// //
this.labelLocales.AutoSize = true; this.labelLocales.AutoSize = true;
this.labelLocales.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold); this.labelLocales.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelLocales.Location = new System.Drawing.Point(0, 236); this.labelLocales.Location = new System.Drawing.Point(0, 0);
this.labelLocales.Margin = new System.Windows.Forms.Padding(0, 27, 0, 1); this.labelLocales.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.labelLocales.Name = "labelLocales"; this.labelLocales.Name = "labelLocales";
this.labelLocales.Size = new System.Drawing.Size(67, 19); this.labelLocales.Size = new System.Drawing.Size(67, 19);
this.labelLocales.TabIndex = 8; this.labelLocales.TabIndex = 0;
this.labelLocales.Text = "LOCALES"; this.labelLocales.Text = "LOCALES";
// //
// checkSpellCheck // checkSpellCheck
// //
this.checkSpellCheck.AutoSize = true; this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Font = new System.Drawing.Font("Segoe UI", 9F); this.checkSpellCheck.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkSpellCheck.Location = new System.Drawing.Point(6, 262); this.checkSpellCheck.Location = new System.Drawing.Point(6, 26);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2); this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkSpellCheck.Name = "checkSpellCheck"; this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(125, 19); this.checkSpellCheck.Size = new System.Drawing.Size(125, 19);
this.checkSpellCheck.TabIndex = 9; this.checkSpellCheck.TabIndex = 1;
this.checkSpellCheck.Text = "Enable Spell Check"; this.checkSpellCheck.Text = "Enable Spell Check";
this.checkSpellCheck.UseVisualStyleBackColor = true; this.checkSpellCheck.UseVisualStyleBackColor = true;
// //
@@ -454,11 +417,11 @@
// //
this.labelSpellCheckLanguage.AutoSize = true; this.labelSpellCheckLanguage.AutoSize = true;
this.labelSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); this.labelSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 295); this.labelSpellCheckLanguage.Location = new System.Drawing.Point(3, 59);
this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage"; this.labelSpellCheckLanguage.Name = "labelSpellCheckLanguage";
this.labelSpellCheckLanguage.Size = new System.Drawing.Size(123, 15); this.labelSpellCheckLanguage.Size = new System.Drawing.Size(123, 15);
this.labelSpellCheckLanguage.TabIndex = 10; this.labelSpellCheckLanguage.TabIndex = 2;
this.labelSpellCheckLanguage.Text = "Spell Check Language"; this.labelSpellCheckLanguage.Text = "Spell Check Language";
// //
// comboBoxSpellCheckLanguage // comboBoxSpellCheckLanguage
@@ -466,21 +429,21 @@
this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxSpellCheckLanguage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F); this.comboBoxSpellCheckLanguage.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxSpellCheckLanguage.FormattingEnabled = true; this.comboBoxSpellCheckLanguage.FormattingEnabled = true;
this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 314); this.comboBoxSpellCheckLanguage.Location = new System.Drawing.Point(5, 78);
this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage"; this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage";
this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(290, 23); this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(290, 23);
this.comboBoxSpellCheckLanguage.TabIndex = 11; this.comboBoxSpellCheckLanguage.TabIndex = 3;
// //
// labelTranslationTarget // labelTranslationTarget
// //
this.labelTranslationTarget.AutoSize = true; this.labelTranslationTarget.AutoSize = true;
this.labelTranslationTarget.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold); this.labelTranslationTarget.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTranslationTarget.Location = new System.Drawing.Point(3, 352); this.labelTranslationTarget.Location = new System.Drawing.Point(3, 116);
this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelTranslationTarget.Name = "labelTranslationTarget"; this.labelTranslationTarget.Name = "labelTranslationTarget";
this.labelTranslationTarget.Size = new System.Drawing.Size(142, 15); this.labelTranslationTarget.Size = new System.Drawing.Size(142, 15);
this.labelTranslationTarget.TabIndex = 12; this.labelTranslationTarget.TabIndex = 4;
this.labelTranslationTarget.Text = "Bing Translator Language"; this.labelTranslationTarget.Text = "Bing Translator Language";
// //
// comboBoxTranslationTarget // comboBoxTranslationTarget
@@ -488,11 +451,114 @@
this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F); this.comboBoxTranslationTarget.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxTranslationTarget.FormattingEnabled = true; this.comboBoxTranslationTarget.FormattingEnabled = true;
this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 371); this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 135);
this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget"; this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget";
this.comboBoxTranslationTarget.Size = new System.Drawing.Size(290, 23); this.comboBoxTranslationTarget.Size = new System.Drawing.Size(290, 23);
this.comboBoxTranslationTarget.TabIndex = 13; this.comboBoxTranslationTarget.TabIndex = 5;
//
// labelFirstDayOfWeek
//
this.labelFirstDayOfWeek.AutoSize = true;
this.labelFirstDayOfWeek.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelFirstDayOfWeek.Location = new System.Drawing.Point(3, 173);
this.labelFirstDayOfWeek.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelFirstDayOfWeek.Name = "labelFirstDayOfWeek";
this.labelFirstDayOfWeek.Size = new System.Drawing.Size(125, 15);
this.labelFirstDayOfWeek.TabIndex = 6;
this.labelFirstDayOfWeek.Text = "First Day Of The Week";
//
// comboBoxFirstDayOfWeek
//
this.comboBoxFirstDayOfWeek.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxFirstDayOfWeek.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxFirstDayOfWeek.FormattingEnabled = true;
this.comboBoxFirstDayOfWeek.Location = new System.Drawing.Point(5, 192);
this.comboBoxFirstDayOfWeek.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxFirstDayOfWeek.Name = "comboBoxFirstDayOfWeek";
this.comboBoxFirstDayOfWeek.Size = new System.Drawing.Size(173, 23);
this.comboBoxFirstDayOfWeek.TabIndex = 7;
//
// labelExternalApplications
//
this.labelExternalApplications.AutoSize = true;
this.labelExternalApplications.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelExternalApplications.Location = new System.Drawing.Point(0, 243);
this.labelExternalApplications.Margin = new System.Windows.Forms.Padding(0, 25, 0, 1);
this.labelExternalApplications.Name = "labelExternalApplications";
this.labelExternalApplications.Size = new System.Drawing.Size(176, 19);
this.labelExternalApplications.TabIndex = 8;
this.labelExternalApplications.Text = "EXTERNAL APPLICATIONS";
//
// panelCustomBrowser
//
this.panelCustomBrowser.Controls.Add(this.comboBoxCustomBrowser);
this.panelCustomBrowser.Controls.Add(this.btnCustomBrowserChange);
this.panelCustomBrowser.Location = new System.Drawing.Point(0, 293);
this.panelCustomBrowser.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.panelCustomBrowser.Name = "panelCustomBrowser";
this.panelCustomBrowser.Size = new System.Drawing.Size(300, 24);
this.panelCustomBrowser.TabIndex = 10;
//
// btnCustomBrowserChange
//
this.btnCustomBrowserChange.AutoSize = true;
this.btnCustomBrowserChange.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnCustomBrowserChange.Location = new System.Drawing.Point(186, 0);
this.btnCustomBrowserChange.Margin = new System.Windows.Forms.Padding(5, 0, 3, 0);
this.btnCustomBrowserChange.Name = "btnCustomBrowserChange";
this.btnCustomBrowserChange.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCustomBrowserChange.Size = new System.Drawing.Size(71, 25);
this.btnCustomBrowserChange.TabIndex = 1;
this.btnCustomBrowserChange.Text = "Change...";
this.btnCustomBrowserChange.UseVisualStyleBackColor = true;
this.btnCustomBrowserChange.Visible = false;
//
// labelVideoPlayerPath
//
this.labelVideoPlayerPath.AutoSize = true;
this.labelVideoPlayerPath.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelVideoPlayerPath.Location = new System.Drawing.Point(3, 332);
this.labelVideoPlayerPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelVideoPlayerPath.Name = "labelVideoPlayerPath";
this.labelVideoPlayerPath.Size = new System.Drawing.Size(106, 15);
this.labelVideoPlayerPath.TabIndex = 11;
this.labelVideoPlayerPath.Text = "Play Videos With...";
//
// panelCustomVideoPlayer
//
this.panelCustomVideoPlayer.Controls.Add(this.comboBoxCustomVideoPlayer);
this.panelCustomVideoPlayer.Controls.Add(this.btnCustomVideoPlayerChange);
this.panelCustomVideoPlayer.Location = new System.Drawing.Point(0, 350);
this.panelCustomVideoPlayer.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
this.panelCustomVideoPlayer.Name = "panelCustomVideoPlayer";
this.panelCustomVideoPlayer.Size = new System.Drawing.Size(300, 24);
this.panelCustomVideoPlayer.TabIndex = 12;
//
// comboBoxCustomVideoPlayer
//
this.comboBoxCustomVideoPlayer.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxCustomVideoPlayer.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxCustomVideoPlayer.FormattingEnabled = true;
this.comboBoxCustomVideoPlayer.Location = new System.Drawing.Point(5, 1);
this.comboBoxCustomVideoPlayer.Margin = new System.Windows.Forms.Padding(5, 1, 3, 0);
this.comboBoxCustomVideoPlayer.Name = "comboBoxCustomVideoPlayer";
this.comboBoxCustomVideoPlayer.Size = new System.Drawing.Size(173, 23);
this.comboBoxCustomVideoPlayer.TabIndex = 0;
//
// btnCustomVideoPlayerChange
//
this.btnCustomVideoPlayerChange.AutoSize = true;
this.btnCustomVideoPlayerChange.Font = new System.Drawing.Font("Segoe UI", 9F);
this.btnCustomVideoPlayerChange.Location = new System.Drawing.Point(186, 0);
this.btnCustomVideoPlayerChange.Margin = new System.Windows.Forms.Padding(5, 0, 3, 0);
this.btnCustomVideoPlayerChange.Name = "btnCustomVideoPlayerChange";
this.btnCustomVideoPlayerChange.Padding = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.btnCustomVideoPlayerChange.Size = new System.Drawing.Size(71, 25);
this.btnCustomVideoPlayerChange.TabIndex = 1;
this.btnCustomVideoPlayerChange.Text = "Change...";
this.btnCustomVideoPlayerChange.UseVisualStyleBackColor = true;
this.btnCustomVideoPlayerChange.Visible = false;
// //
// panelSeparator // panelSeparator
// //
@@ -520,6 +586,10 @@
this.flowPanelLeft.PerformLayout(); this.flowPanelLeft.PerformLayout();
this.flowPanelRight.ResumeLayout(false); this.flowPanelRight.ResumeLayout(false);
this.flowPanelRight.PerformLayout(); this.flowPanelRight.PerformLayout();
this.panelCustomBrowser.ResumeLayout(false);
this.panelCustomBrowser.PerformLayout();
this.panelCustomVideoPlayer.ResumeLayout(false);
this.panelCustomVideoPlayer.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@@ -543,7 +613,7 @@
private System.Windows.Forms.FlowLayoutPanel flowPanelLeft; private System.Windows.Forms.FlowLayoutPanel flowPanelLeft;
private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen; private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen;
private System.Windows.Forms.Label labelBrowserPath; private System.Windows.Forms.Label labelBrowserPath;
private System.Windows.Forms.ComboBox comboBoxBrowserPath; private System.Windows.Forms.ComboBox comboBoxCustomBrowser;
private System.Windows.Forms.Label labelBrowserSettings; private System.Windows.Forms.Label labelBrowserSettings;
private System.Windows.Forms.CheckBox checkSmoothScrolling; private System.Windows.Forms.CheckBox checkSmoothScrolling;
private System.Windows.Forms.Label labelSearchEngine; private System.Windows.Forms.Label labelSearchEngine;
@@ -551,10 +621,6 @@
private System.Windows.Forms.CheckBox checkTouchAdjustment; private System.Windows.Forms.CheckBox checkTouchAdjustment;
private System.Windows.Forms.FlowLayoutPanel flowPanelRight; private System.Windows.Forms.FlowLayoutPanel flowPanelRight;
private System.Windows.Forms.Panel panelSeparator; private System.Windows.Forms.Panel panelSeparator;
private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.Label labelLocales; private System.Windows.Forms.Label labelLocales;
private System.Windows.Forms.CheckBox checkSpellCheck; private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.Label labelSpellCheckLanguage; private System.Windows.Forms.Label labelSpellCheckLanguage;
@@ -563,5 +629,14 @@
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; private System.Windows.Forms.CheckBox checkFocusDmInput;
private System.Windows.Forms.Panel panelCustomBrowser;
private System.Windows.Forms.Button btnCustomBrowserChange;
private System.Windows.Forms.Label labelVideoPlayerPath;
private System.Windows.Forms.Panel panelCustomVideoPlayer;
private System.Windows.Forms.ComboBox comboBoxCustomVideoPlayer;
private System.Windows.Forms.Button btnCustomVideoPlayerChange;
private System.Windows.Forms.Label labelExternalApplications;
private System.Windows.Forms.Label labelFirstDayOfWeek;
private System.Windows.Forms.ComboBox comboBoxFirstDayOfWeek;
} }
} }

View File

@@ -6,7 +6,8 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Updates; using TweetLib.Core.Features.Updates;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsGeneral : BaseTabSettings{ sealed partial class TabSettingsGeneral : BaseTabSettings{
@@ -18,6 +19,9 @@ namespace TweetDuck.Core.Other.Settings{
private readonly int browserListIndexDefault; private readonly int browserListIndexDefault;
private readonly int browserListIndexCustom; private readonly int browserListIndexCustom;
private readonly int videoPlayerListIndexDefault;
private readonly int videoPlayerListIndexCustom;
private readonly int searchEngineIndexDefault; private readonly int searchEngineIndexDefault;
private readonly int searchEngineIndexCustom; private readonly int searchEngineIndexCustom;
@@ -50,22 +54,7 @@ namespace TweetDuck.Core.Other.Settings{
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages; checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
trackBarZoom.SetValueSafe(Config.ZoomLevel); trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value + "%";
// system tray
toolTip.SetToolTip(comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
toolTip.SetToolTip(checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count-1);
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
// updates // updates
@@ -79,7 +68,8 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling."); toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling.");
toolTip.SetToolTip(checkTouchAdjustment, "Toggles Chromium touch screen adjustment.\r\nDisabled by default, because it is very imprecise with TweetDeck."); toolTip.SetToolTip(checkTouchAdjustment, "Toggles Chromium touch screen adjustment.\r\nDisabled by default, because it is very imprecise with TweetDeck.");
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance.\r\nDisable if you experience visual glitches, or to save a small amount of RAM."); toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance.\r\nDisable if you experience visual glitches, or to save a small amount of RAM.");
toolTip.SetToolTip(comboBoxBrowserPath, "Sets the default browser for opening links."); toolTip.SetToolTip(comboBoxCustomBrowser, "Sets the default browser for opening links.");
toolTip.SetToolTip(comboBoxCustomVideoPlayer, "Sets the default application for playing videos.");
toolTip.SetToolTip(comboBoxSearchEngine, "Sets the default website for opening searches."); toolTip.SetToolTip(comboBoxSearchEngine, "Sets the default website for opening searches.");
checkSmoothScrolling.Checked = Config.EnableSmoothScrolling; checkSmoothScrolling.Checked = Config.EnableSmoothScrolling;
@@ -87,13 +77,17 @@ namespace TweetDuck.Core.Other.Settings{
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration; checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
foreach(WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()){ foreach(WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()){
comboBoxBrowserPath.Items.Add(browserInfo); comboBoxCustomBrowser.Items.Add(browserInfo);
} }
browserListIndexDefault = comboBoxBrowserPath.Items.Add("(default browser)"); browserListIndexDefault = comboBoxCustomBrowser.Items.Add("(default browser)");
browserListIndexCustom = comboBoxBrowserPath.Items.Add("(custom program...)"); browserListIndexCustom = comboBoxCustomBrowser.Items.Add("(custom program...)");
UpdateBrowserPathSelection(); UpdateBrowserPathSelection();
videoPlayerListIndexDefault = comboBoxCustomVideoPlayer.Items.Add("(default TweetDuck player)");
videoPlayerListIndexCustom = comboBoxCustomVideoPlayer.Items.Add("(custom program...)");
UpdateVideoPlayerPathSelection();
comboBoxSearchEngine.Items.Add(new SearchEngine("DuckDuckGo", "https://duckduckgo.com/?q=")); comboBoxSearchEngine.Items.Add(new SearchEngine("DuckDuckGo", "https://duckduckgo.com/?q="));
comboBoxSearchEngine.Items.Add(new SearchEngine("Google", "https://www.google.com/search?q=")); comboBoxSearchEngine.Items.Add(new SearchEngine("Google", "https://www.google.com/search?q="));
comboBoxSearchEngine.Items.Add(new SearchEngine("Bing", "https://www.bing.com/search?q=")); comboBoxSearchEngine.Items.Add(new SearchEngine("Bing", "https://www.bing.com/search?q="));
@@ -107,6 +101,7 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(checkSpellCheck, "Underlines words that are spelled incorrectly."); toolTip.SetToolTip(checkSpellCheck, "Underlines words that are spelled incorrectly.");
toolTip.SetToolTip(comboBoxSpellCheckLanguage, "Language used for spell check."); toolTip.SetToolTip(comboBoxSpellCheckLanguage, "Language used for spell check.");
toolTip.SetToolTip(comboBoxTranslationTarget, "Language tweets are translated into."); toolTip.SetToolTip(comboBoxTranslationTarget, "Language tweets are translated into.");
toolTip.SetToolTip(comboBoxFirstDayOfWeek, "First day of week used in the date picker.");
checkSpellCheck.Checked = Config.EnableSpellCheck; checkSpellCheck.Checked = Config.EnableSpellCheck;
@@ -125,6 +120,17 @@ namespace TweetDuck.Core.Other.Settings{
} }
comboBoxTranslationTarget.SelectedItem = new LocaleUtils.Item(Config.TranslationTarget); comboBoxTranslationTarget.SelectedItem = new LocaleUtils.Item(Config.TranslationTarget);
var daysOfWeek = comboBoxFirstDayOfWeek.Items;
daysOfWeek.Add("(based on system locale)");
daysOfWeek.Add(new DayOfWeekItem("Monday", DayOfWeek.Monday));
daysOfWeek.Add(new DayOfWeekItem("Tuesday", DayOfWeek.Tuesday));
daysOfWeek.Add(new DayOfWeekItem("Wednesday", DayOfWeek.Wednesday));
daysOfWeek.Add(new DayOfWeekItem("Thursday", DayOfWeek.Thursday));
daysOfWeek.Add(new DayOfWeekItem("Friday", DayOfWeek.Friday));
daysOfWeek.Add(new DayOfWeekItem("Saturday", DayOfWeek.Saturday));
daysOfWeek.Add(new DayOfWeekItem("Sunday", DayOfWeek.Sunday));
comboBoxFirstDayOfWeek.SelectedItem = daysOfWeek.OfType<DayOfWeekItem>().FirstOrDefault(dow => dow.Id == Config.CalendarFirstDay) ?? daysOfWeek[0];
} }
public override void OnReady(){ public override void OnReady(){
@@ -136,21 +142,22 @@ namespace TweetDuck.Core.Other.Settings{
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged; checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
btnCheckUpdates.Click += btnCheckUpdates_Click; btnCheckUpdates.Click += btnCheckUpdates_Click;
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged; checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
checkTouchAdjustment.CheckedChanged += checkTouchAdjustment_CheckedChanged; checkTouchAdjustment.CheckedChanged += checkTouchAdjustment_CheckedChanged;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged; checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged; comboBoxCustomBrowser.SelectedIndexChanged += comboBoxCustomBrowser_SelectedIndexChanged;
btnCustomBrowserChange.Click += btnCustomBrowserChange_Click;
comboBoxCustomVideoPlayer.SelectedIndexChanged += comboBoxCustomVideoPlayer_SelectedIndexChanged;
btnCustomVideoPlayerChange.Click += btnCustomVideoPlayerChange_Click;
comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged; comboBoxSearchEngine.SelectedIndexChanged += comboBoxSearchEngine_SelectedIndexChanged;
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
comboBoxSpellCheckLanguage.SelectedValueChanged += comboBoxSpellCheckLanguage_SelectedValueChanged; comboBoxSpellCheckLanguage.SelectedValueChanged += comboBoxSpellCheckLanguage_SelectedValueChanged;
comboBoxTranslationTarget.SelectedValueChanged += comboBoxTranslationTarget_SelectedValueChanged; comboBoxTranslationTarget.SelectedValueChanged += comboBoxTranslationTarget_SelectedValueChanged;
comboBoxFirstDayOfWeek.SelectedValueChanged += comboBoxFirstDayOfWeek_SelectedValueChanged;
} }
public override void OnClosing(){ public override void OnClosing(){
@@ -188,7 +195,7 @@ namespace TweetDuck.Core.Other.Settings{
if (trackBarZoom.AlignValueToTick()){ if (trackBarZoom.AlignValueToTick()){
zoomUpdateTimer.Stop(); zoomUpdateTimer.Stop();
zoomUpdateTimer.Start(); zoomUpdateTimer.Start();
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value + "%";
} }
} }
@@ -197,18 +204,6 @@ namespace TweetDuck.Core.Other.Settings{
zoomUpdateTimer.Stop(); zoomUpdateTimer.Stop();
} }
#endregion
#region System Tray
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
#endregion #endregion
#region Updates #region Updates
@@ -252,43 +247,95 @@ namespace TweetDuck.Core.Other.Settings{
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked; SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
} }
private void UpdateBrowserChangeButton(){
btnCustomBrowserChange.Visible = comboBoxCustomBrowser.SelectedIndex == browserListIndexCustom;
}
private void UpdateBrowserPathSelection(){ private void UpdateBrowserPathSelection(){
if (string.IsNullOrEmpty(Config.BrowserPath) || !File.Exists(Config.BrowserPath)){ if (string.IsNullOrEmpty(Config.BrowserPath) || !File.Exists(Config.BrowserPath)){
comboBoxBrowserPath.SelectedIndex = browserListIndexDefault; comboBoxCustomBrowser.SelectedIndex = browserListIndexDefault;
} }
else{ else{
WindowsUtils.Browser browserInfo = comboBoxBrowserPath.Items.OfType<WindowsUtils.Browser>().FirstOrDefault(browser => browser.Path == Config.BrowserPath); WindowsUtils.Browser browserInfo = comboBoxCustomBrowser.Items.OfType<WindowsUtils.Browser>().FirstOrDefault(browser => browser.Path == Config.BrowserPath);
if (browserInfo == null){ if (browserInfo == null || Config.BrowserPathArgs != null){
comboBoxBrowserPath.SelectedIndex = browserListIndexCustom; comboBoxCustomBrowser.SelectedIndex = browserListIndexCustom;
} }
else{ else{
comboBoxBrowserPath.SelectedItem = browserInfo; comboBoxCustomBrowser.SelectedItem = browserInfo;
} }
} }
UpdateBrowserChangeButton();
}
private void comboBoxCustomBrowser_SelectedIndexChanged(object sender, EventArgs e){
if (comboBoxCustomBrowser.SelectedIndex == browserListIndexCustom){
btnCustomBrowserChange_Click(sender, e);
}
else{
Config.BrowserPath = (comboBoxCustomBrowser.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null
Config.BrowserPathArgs = null;
UpdateBrowserChangeButton();
}
} }
private void comboBoxBrowserPath_SelectedIndexChanged(object sender, EventArgs e){ private void btnCustomBrowserChange_Click(object sender, EventArgs e){
if (comboBoxBrowserPath.SelectedIndex == browserListIndexCustom){ using(DialogSettingsExternalProgram dialog = new DialogSettingsExternalProgram("External Browser", "Open Links With..."){
using(OpenFileDialog dialog = new OpenFileDialog{ Path = Config.BrowserPath,
AutoUpgradeEnabled = true, Args = Config.BrowserPathArgs
DereferenceLinks = true, }){
InitialDirectory = Path.GetDirectoryName(Config.BrowserPath), // returns null if argument is null if (dialog.ShowDialog() == DialogResult.OK){
Title = "Open Links With...", Config.BrowserPath = dialog.Path;
Filter = "Executables (*.exe;*.bat;*.cmd)|*.exe;*.bat;*.cmd|All Files (*.*)|*.*" Config.BrowserPathArgs = dialog.Args;
}){
if (dialog.ShowDialog() == DialogResult.OK){
Config.BrowserPath = dialog.FileName;
}
} }
}
comboBoxBrowserPath.SelectedIndexChanged -= comboBoxBrowserPath_SelectedIndexChanged; comboBoxCustomBrowser.SelectedIndexChanged -= comboBoxCustomBrowser_SelectedIndexChanged;
UpdateBrowserPathSelection(); UpdateBrowserPathSelection();
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged; comboBoxCustomBrowser.SelectedIndexChanged += comboBoxCustomBrowser_SelectedIndexChanged;
}
private void UpdateVideoPlayerChangeButton(){
btnCustomVideoPlayerChange.Visible = comboBoxCustomVideoPlayer.SelectedIndex == videoPlayerListIndexCustom;
}
private void UpdateVideoPlayerPathSelection(){
if (string.IsNullOrEmpty(Config.VideoPlayerPath) || !File.Exists(Config.VideoPlayerPath)){
comboBoxCustomVideoPlayer.SelectedIndex = videoPlayerListIndexDefault;
} }
else{ else{
Config.BrowserPath = (comboBoxBrowserPath.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null comboBoxCustomVideoPlayer.SelectedIndex = videoPlayerListIndexCustom;
} }
UpdateVideoPlayerChangeButton();
}
private void comboBoxCustomVideoPlayer_SelectedIndexChanged(object sender, EventArgs e){
if (comboBoxCustomVideoPlayer.SelectedIndex == videoPlayerListIndexCustom){
btnCustomVideoPlayerChange_Click(sender, e);
}
else{
Config.VideoPlayerPath = null;
Config.VideoPlayerPathArgs = null;
UpdateVideoPlayerChangeButton();
}
}
private void btnCustomVideoPlayerChange_Click(object sender, EventArgs e){
using(DialogSettingsExternalProgram dialog = new DialogSettingsExternalProgram("External Video Player", "Play Videos With..."){
Path = Config.VideoPlayerPath,
Args = Config.VideoPlayerPathArgs
}){
if (dialog.ShowDialog() == DialogResult.OK){
Config.VideoPlayerPath = dialog.Path;
Config.VideoPlayerPathArgs = dialog.Args;
}
}
comboBoxCustomVideoPlayer.SelectedIndexChanged -= comboBoxCustomVideoPlayer_SelectedIndexChanged;
UpdateVideoPlayerPathSelection();
comboBoxCustomVideoPlayer.SelectedIndexChanged += comboBoxCustomVideoPlayer_SelectedIndexChanged;
} }
private void comboBoxSearchEngine_SelectedIndexChanged(object sender, EventArgs e){ private void comboBoxSearchEngine_SelectedIndexChanged(object sender, EventArgs e){
@@ -354,6 +401,24 @@ namespace TweetDuck.Core.Other.Settings{
Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code ?? "en"; Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code ?? "en";
} }
private void comboBoxFirstDayOfWeek_SelectedValueChanged(object sender, EventArgs e){
Config.CalendarFirstDay = (comboBoxFirstDayOfWeek.SelectedItem as DayOfWeekItem)?.Id ?? -1;
}
private sealed class DayOfWeekItem{
private string Name { get; }
public int Id { get; }
public DayOfWeekItem(string name, DayOfWeek dow){
Name = name;
Id = LocaleUtils.GetJQueryDayOfWeek(dow);
}
public override int GetHashCode() => Name.GetHashCode();
public override bool Equals(object obj) => obj is DayOfWeekItem other && Name == other.Name;
public override string ToString() => Name;
}
#endregion #endregion
} }
} }

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Example; using TweetDuck.Core.Notification.Example;
using TweetLib.Core.Features.Notifications;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsNotifications : BaseTabSettings{ sealed partial class TabSettingsNotifications : BaseTabSettings{
@@ -59,31 +59,31 @@ namespace TweetDuck.Core.Other.Settings{
checkTimerCountDown.Checked = Config.NotificationTimerCountDown; checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
trackBarDuration.SetValueSafe(Config.NotificationDurationValue); trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; labelDurationValue.Text = Config.NotificationDurationValue + " ms/c";
// location // location
toolTip.SetToolTip(radioLocCustom, "Drag the example notification window to the desired location."); toolTip.SetToolTip(radioLocCustom, "Drag the example notification window to the desired location.");
switch(Config.NotificationPosition){ switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break; case DesktopNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break; case DesktopNotification.Position.TopRight: radioLocTR.Checked = true; break;
case TweetNotification.Position.BottomLeft: radioLocBL.Checked = true; break; case DesktopNotification.Position.BottomLeft: radioLocBL.Checked = true; break;
case TweetNotification.Position.BottomRight: radioLocBR.Checked = true; break; case DesktopNotification.Position.BottomRight: radioLocBR.Checked = true; break;
case TweetNotification.Position.Custom: radioLocCustom.Checked = true; break; case DesktopNotification.Position.Custom: radioLocCustom.Checked = true; break;
} }
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
comboBoxDisplay.Items.Add("(Same as TweetDuck)"); comboBoxDisplay.Items.Add("(Same as TweetDuck)");
foreach(Screen screen in Screen.AllScreens){ foreach(Screen screen in Screen.AllScreens){
comboBoxDisplay.Items.Add(screen.DeviceName.TrimStart('\\', '.')+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")"); comboBoxDisplay.Items.Add($"{screen.DeviceName.TrimStart('\\', '.')} ({screen.Bounds.Width}x{screen.Bounds.Height})");
} }
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay); comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count - 1, Config.NotificationDisplay);
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance); trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value + " px";
// size // size
@@ -91,12 +91,12 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(radioSizeCustom, "Resize the example notification window to the desired size."); toolTip.SetToolTip(radioSizeCustom, "Resize the example notification window to the desired size.");
switch(Config.NotificationSize){ switch(Config.NotificationSize){
case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break; case DesktopNotification.Size.Auto: radioSizeAuto.Checked = true; break;
case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break; case DesktopNotification.Size.Custom: radioSizeCustom.Checked = true; break;
} }
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed); trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%"; labelScrollSpeedValue.Text = trackBarScrollSpeed.Value + "%";
} }
public override void OnReady(){ public override void OnReady(){
@@ -195,7 +195,7 @@ namespace TweetDuck.Core.Other.Settings{
durationUpdateTimer.Start(); durationUpdateTimer.Start();
Config.NotificationDurationValue = trackBarDuration.Value; Config.NotificationDurationValue = trackBarDuration.Value;
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c"; labelDurationValue.Text = Config.NotificationDurationValue + " ms/c";
} }
private void btnDurationShort_Click(object sender, EventArgs e){ private void btnDurationShort_Click(object sender, EventArgs e){
@@ -219,10 +219,10 @@ namespace TweetDuck.Core.Other.Settings{
#region Location #region Location
private void radioLoc_CheckedChanged(object sender, EventArgs e){ private void radioLoc_CheckedChanged(object sender, EventArgs e){
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft; if (radioLocTL.Checked)Config.NotificationPosition = DesktopNotification.Position.TopLeft;
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight; else if (radioLocTR.Checked)Config.NotificationPosition = DesktopNotification.Position.TopRight;
else if (radioLocBL.Checked)Config.NotificationPosition = TweetNotification.Position.BottomLeft; else if (radioLocBL.Checked)Config.NotificationPosition = DesktopNotification.Position.BottomLeft;
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight; else if (radioLocBR.Checked)Config.NotificationPosition = DesktopNotification.Position.BottomRight;
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
notification.ShowExampleNotification(false); notification.ShowExampleNotification(false);
@@ -233,18 +233,18 @@ namespace TweetDuck.Core.Other.Settings{
Config.CustomNotificationPosition = notification.Location; Config.CustomNotificationPosition = notification.Location;
} }
Config.NotificationPosition = TweetNotification.Position.Custom; Config.NotificationPosition = DesktopNotification.Position.Custom;
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
notification.ShowExampleNotification(false); notification.ShowExampleNotification(false);
if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is Outside View", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){ if (notification.IsFullyOutsideView() && FormMessage.Question("Notification is Outside View", "The notification seems to be outside of view, would you like to reset its position?", FormMessage.Yes, FormMessage.No)){
Config.NotificationPosition = TweetNotification.Position.TopRight; Config.NotificationPosition = DesktopNotification.Position.TopRight;
notification.MoveToVisibleLocation(); notification.MoveToVisibleLocation();
Config.CustomNotificationPosition = notification.Location; Config.CustomNotificationPosition = notification.Location;
Config.NotificationPosition = TweetNotification.Position.Custom; Config.NotificationPosition = DesktopNotification.Position.Custom;
notification.MoveToVisibleLocation(); notification.MoveToVisibleLocation();
} }
} }
@@ -255,7 +255,7 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){ private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value + " px";
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value; Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
notification.ShowExampleNotification(false); notification.ShowExampleNotification(false);
} }
@@ -265,7 +265,7 @@ namespace TweetDuck.Core.Other.Settings{
private void radioSize_CheckedChanged(object sender, EventArgs e){ private void radioSize_CheckedChanged(object sender, EventArgs e){
if (radioSizeAuto.Checked){ if (radioSizeAuto.Checked){
Config.NotificationSize = TweetNotification.Size.Auto; Config.NotificationSize = DesktopNotification.Size.Auto;
} }
notification.ShowExampleNotification(false); notification.ShowExampleNotification(false);
@@ -276,13 +276,13 @@ namespace TweetDuck.Core.Other.Settings{
Config.CustomNotificationSize = notification.BrowserSize; Config.CustomNotificationSize = notification.BrowserSize;
} }
Config.NotificationSize = TweetNotification.Size.Custom; Config.NotificationSize = DesktopNotification.Size.Custom;
notification.ShowExampleNotification(false); notification.ShowExampleNotification(false);
} }
private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){ private void trackBarScrollSpeed_ValueChanged(object sender, EventArgs e){
if (trackBarScrollSpeed.AlignValueToTick()){ if (trackBarScrollSpeed.AlignValueToTick()){
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%"; labelScrollSpeedValue.Text = trackBarScrollSpeed.Value + "%";
Config.NotificationScrollSpeed = trackBarScrollSpeed.Value; Config.NotificationScrollSpeed = trackBarScrollSpeed.Value;
} }
} }

View File

@@ -20,7 +20,7 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(tbCustomSound, "When empty, the default TweetDeck sound notification is used."); toolTip.SetToolTip(tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume); trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
labelVolumeValue.Text = trackBarVolume.Value+"%"; labelVolumeValue.Text = trackBarVolume.Value + "%";
tbCustomSound.Text = Config.NotificationSoundPath; tbCustomSound.Text = Config.NotificationSoundPath;
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty); tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
@@ -83,7 +83,7 @@ namespace TweetDuck.Core.Other.Settings{
private void trackBarVolume_ValueChanged(object sender, EventArgs e){ private void trackBarVolume_ValueChanged(object sender, EventArgs e){
volumeUpdateTimer.Stop(); volumeUpdateTimer.Stop();
volumeUpdateTimer.Start(); volumeUpdateTimer.Start();
labelVolumeValue.Text = trackBarVolume.Value+"%"; labelVolumeValue.Text = trackBarVolume.Value + "%";
} }
private void volumeUpdateTimer_Tick(object sender, EventArgs e){ private void volumeUpdateTimer_Tick(object sender, EventArgs e){

View File

@@ -0,0 +1,131 @@
namespace TweetDuck.Core.Other.Settings {
partial class TabSettingsTray {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.flowPanelLeft = new System.Windows.Forms.FlowLayoutPanel();
this.labelTray = new System.Windows.Forms.Label();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayIcon = new System.Windows.Forms.Label();
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.flowPanelRight = new System.Windows.Forms.FlowLayoutPanel();
this.flowPanelLeft.SuspendLayout();
this.SuspendLayout();
//
// flowPanelLeft
//
this.flowPanelLeft.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.flowPanelLeft.Controls.Add(this.labelTray);
this.flowPanelLeft.Controls.Add(this.comboBoxTrayType);
this.flowPanelLeft.Controls.Add(this.labelTrayIcon);
this.flowPanelLeft.Controls.Add(this.checkTrayHighlight);
this.flowPanelLeft.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanelLeft.Location = new System.Drawing.Point(9, 9);
this.flowPanelLeft.Name = "flowPanelLeft";
this.flowPanelLeft.Size = new System.Drawing.Size(300, 462);
this.flowPanelLeft.TabIndex = 0;
this.flowPanelLeft.WrapContents = false;
//
// labelTray
//
this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold);
this.labelTray.Location = new System.Drawing.Point(0, 0);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(99, 19);
this.labelTray.TabIndex = 0;
this.labelTray.Text = "SYSTEM TRAY";
//
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.Font = new System.Drawing.Font("Segoe UI", 9F);
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 24);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 23);
this.comboBoxTrayType.TabIndex = 1;
//
// labelTrayIcon
//
this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold);
this.labelTrayIcon.Location = new System.Drawing.Point(3, 59);
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(56, 15);
this.labelTrayIcon.TabIndex = 2;
this.labelTrayIcon.Text = "Tray Icon";
//
// checkTrayHighlight
//
this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Font = new System.Drawing.Font("Segoe UI", 9F);
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 80);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 2);
this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(114, 19);
this.checkTrayHighlight.TabIndex = 3;
this.checkTrayHighlight.Text = "Enable Highlight";
this.checkTrayHighlight.UseVisualStyleBackColor = true;
//
// flowPanelRight
//
this.flowPanelRight.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)));
this.flowPanelRight.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanelRight.Location = new System.Drawing.Point(322, 9);
this.flowPanelRight.Name = "flowPanelRight";
this.flowPanelRight.Size = new System.Drawing.Size(300, 462);
this.flowPanelRight.TabIndex = 1;
this.flowPanelRight.WrapContents = false;
//
// TabSettingsTray
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.flowPanelRight);
this.Controls.Add(this.flowPanelLeft);
this.Name = "TabSettingsTray";
this.Size = new System.Drawing.Size(631, 480);
this.flowPanelLeft.ResumeLayout(false);
this.flowPanelLeft.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.FlowLayoutPanel flowPanelLeft;
private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.FlowLayoutPanel flowPanelRight;
}
}

View File

@@ -0,0 +1,42 @@
using System;
namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsTray : BaseTabSettings{
public TabSettingsTray(){
InitializeComponent();
// system tray
toolTip.SetToolTip(comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
toolTip.SetToolTip(checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.Items.Add("Combined");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior, 0), comboBoxTrayType.Items.Count - 1);
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
}
public override void OnReady(){
comboBoxTrayType.SelectedIndexChanged += comboBoxTrayType_SelectedIndexChanged;
checkTrayHighlight.CheckedChanged += checkTrayHighlight_CheckedChanged;
}
#region System Tray
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
checkTrayHighlight.Enabled = Config.TrayBehavior.ShouldDisplayIcon();
}
private void checkTrayHighlight_CheckedChanged(object sender, EventArgs e){
Config.EnableTrayHighlight = checkTrayHighlight.Checked;
}
#endregion
}
}

View File

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

View File

@@ -51,7 +51,7 @@ namespace TweetDuck.Core.Other{
this.contextMenu.MenuItems.Add("Mute notifications", menuItemMuteNotifications_Click); this.contextMenu.MenuItems.Add("Mute notifications", menuItemMuteNotifications_Click);
this.contextMenu.MenuItems.Add("Close", menuItemClose_Click); this.contextMenu.MenuItems.Add("Close", menuItemClose_Click);
this.contextMenu.Popup += contextMenu_Popup; this.contextMenu.Popup += contextMenu_Popup;
this.notifyIcon.ContextMenu = contextMenu; this.notifyIcon.ContextMenu = contextMenu;
this.notifyIcon.Text = Program.BrandName; this.notifyIcon.Text = Program.BrandName;

View File

@@ -5,6 +5,7 @@ using System.Windows.Forms;
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Adapters;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
@@ -12,8 +13,10 @@ using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetLib.Core.Features.Plugins;
using TweetDuck.Resources; using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Twitter;
using TweetLib.Core.Utils;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{ sealed class TweetDeckBrowser : IDisposable{
@@ -35,9 +38,8 @@ namespace TweetDuck.Core{
return false; return false;
} }
using(IFrame frame = browser.GetBrowser().MainFrame){ using IFrame frame = browser.GetBrowser().MainFrame;
return TwitterUtils.IsTweetDeckWebsite(frame); return TwitterUrls.IsTweetDeck(frame.Url);
}
} }
} }
@@ -47,12 +49,12 @@ namespace TweetDuck.Core{
private string prevSoundNotificationPath = null; private string prevSoundNotificationPath = null;
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){ public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge tdBridge, UpdateBridge updateBridge){
resourceHandlerFactory.RegisterHandler(TweetNotification.AppLogo); resourceHandlerFactory.RegisterHandler(FormNotificationBase.AppLogo);
resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner); resourceHandlerFactory.RegisterHandler(TwitterUtils.LoadingSpinner);
RequestHandlerBrowser requestHandler = new RequestHandlerBrowser(); RequestHandlerBrowser requestHandler = new RequestHandlerBrowser();
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){ this.browser = new ChromiumWebBrowser(TwitterUrls.TweetDeck){
DialogHandler = new FileDialogHandler(), DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(requestHandler), DragHandler = new DragHandlerBrowser(requestHandler),
MenuHandler = new ContextMenuBrowser(owner), MenuHandler = new ContextMenuBrowser(owner),
@@ -77,7 +79,7 @@ namespace TweetDuck.Core{
this.browser.SetupZoomEvents(); this.browser.SetupZoomEvents();
owner.Controls.Add(browser); owner.Controls.Add(browser);
plugins.Register(browser, PluginEnvironment.Browser, owner, true); plugins.Register(PluginEnvironment.Browser, new PluginDispatcher(browser));
Config.MuteToggled += Config_MuteToggled; Config.MuteToggled += Config_MuteToggled;
Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged; Config.SoundNotificationChanged += Config_SoundNotificationInfoChanged;
@@ -121,14 +123,16 @@ namespace TweetDuck.Core{
IFrame frame = e.Frame; IFrame frame = e.Frame;
if (frame.IsMain){ if (frame.IsMain){
if (TwitterUtils.IsTwitterWebsite(frame)){ string url = frame.Url;
string css = ScriptLoader.LoadResource("styles/twitter.css", browser);
if (TwitterUrls.IsTwitter(url)){
string css = Program.Resources.Load("styles/twitter.css");
resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css")); resourceHandlerFactory.RegisterHandler(TwitterStyleUrl, ResourceHandler.FromString(css, mimeType: "text/css"));
ScriptLoader.ExecuteFile(frame, "twitter.js", browser); CefScriptExecutor.RunFile(frame, "twitter.js");
} }
if (!TwitterUtils.IsTwitterLogin2FactorWebsite(frame)){ if (!TwitterUrls.IsTwitterLogin2Factor(url)){
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride); frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
} }
} }
@@ -136,11 +140,12 @@ namespace TweetDuck.Core{
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
IFrame frame = e.Frame; IFrame frame = e.Frame;
string url = frame.Url;
if (frame.IsMain){ if (frame.IsMain){
if (TwitterUtils.IsTweetDeckWebsite(frame)){ if (TwitterUrls.IsTweetDeck(url)){
UpdateProperties(); UpdateProperties();
ScriptLoader.ExecuteFile(frame, "code.js", browser); CefScriptExecutor.RunFile(frame, "code.js");
InjectBrowserCSS(); InjectBrowserCSS();
ReinjectCustomCSS(Config.CustomBrowserCSS); ReinjectCustomCSS(Config.CustomBrowserCSS);
@@ -149,18 +154,18 @@ namespace TweetDuck.Core{
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){ if (Arguments.HasFlag(Arguments.ArgIgnoreGDPR)){
ScriptLoader.ExecuteScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr"); CefScriptExecutor.RunScript(frame, "TD.storage.Account.prototype.requiresConsent = function(){ return false; }", "gen:gdpr");
} }
if (Config.FirstRun){ if (Config.FirstRun){
ScriptLoader.ExecuteFile(frame, "introduction.js", browser); CefScriptExecutor.RunFile(frame, "introduction.js");
} }
} }
ScriptLoader.ExecuteFile(frame, "update.js", browser); CefScriptExecutor.RunFile(frame, "update.js");
} }
if (frame.Url == ErrorUrl){ if (url == ErrorUrl){
resourceHandlerFactory.UnregisterHandler(ErrorUrl); resourceHandlerFactory.UnregisterHandler(ErrorUrl);
} }
} }
@@ -171,10 +176,13 @@ namespace TweetDuck.Core{
} }
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){ if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResourceSilent("pages/error.html"); string errorPage = Program.Resources.LoadSilent("pages/error.html");
if (errorPage != null){ if (errorPage != null){
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)))); string errorName = Enum.GetName(typeof(CefErrorCode), e.ErrorCode);
string errorTitle = StringUtils.ConvertPascalCaseToScreamingSnakeCase(errorName ?? string.Empty);
resourceHandlerFactory.RegisterHandler(ErrorUrl, ResourceHandler.FromString(errorPage.Replace("{err}", errorTitle)));
browser.Load(ErrorUrl); browser.Load(ErrorUrl);
} }
} }
@@ -217,7 +225,7 @@ namespace TweetDuck.Core{
// javascript calls // javascript calls
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'"); browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUrls.TweetDeck}'");
} }
public void UpdateProperties(){ public void UpdateProperties(){
@@ -225,7 +233,7 @@ namespace TweetDuck.Core{
} }
public void InjectBrowserCSS(){ public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css", browser)?.TrimEnd() ?? string.Empty); browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", Program.Resources.Load("styles/browser.css")?.TrimEnd() ?? string.Empty);
} }
public void ReinjectCustomCSS(string css){ public void ReinjectCustomCSS(string css){
@@ -265,7 +273,7 @@ namespace TweetDuck.Core{
} }
public void OpenDevTools(){ public void OpenDevTools(){
browser.ShowDevTools(); browser.OpenDevToolsCustom();
} }
} }
} }

View File

@@ -3,16 +3,16 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp.WinForms; using CefSharp.WinForms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public static string UserAgentVanilla => Program.BrandName+" "+Application.ProductVersion; public static string UserAgentVanilla => Program.BrandName + " " + Application.ProductVersion;
public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"+Cef.ChromiumVersion+" Safari/537.36"; public static string UserAgentChrome => "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + Cef.ChromiumVersion + " Safari/537.36";
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"));
@@ -29,7 +29,7 @@ namespace TweetDuck.Core.Utils{
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)){
args["disable-features"] = "TouchpadAndWheelScrollLatching,"+disabledFeatures; args["disable-features"] = "TouchpadAndWheelScrollLatching," + disabledFeatures;
} }
else{ else{
args["disable-features"] = "TouchpadAndWheelScrollLatching"; args["disable-features"] = "TouchpadAndWheelScrollLatching";
@@ -48,7 +48,7 @@ namespace TweetDuck.Core.Utils{
args["enable-system-flash"] = "0"; args["enable-system-flash"] = "0";
if (args.TryGetValue("js-flags", out string jsFlags)){ if (args.TryGetValue("js-flags", out string jsFlags)){
args["js-flags"] = "--expose-gc "+jsFlags; args["js-flags"] = "--expose-gc " + jsFlags;
} }
else{ else{
args["js-flags"] = "--expose-gc"; args["js-flags"] = "--expose-gc";
@@ -60,8 +60,12 @@ namespace TweetDuck.Core.Utils{
} }
public static void SetupZoomEvents(this ChromiumWebBrowser browser){ public static void SetupZoomEvents(this ChromiumWebBrowser browser){
static void SetZoomLevel(IBrowserHost host, int percentage){
host.SetZoomLevel(Math.Log(percentage / 100.0, 1.2));
}
void UpdateZoomLevel(object sender, EventArgs args){ void UpdateZoomLevel(object sender, EventArgs args){
SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel); SetZoomLevel(browser.GetBrowserHost(), Config.ZoomLevel);
} }
Config.ZoomLevelChanged += UpdateZoomLevel; Config.ZoomLevelChanged += UpdateZoomLevel;
@@ -69,34 +73,29 @@ namespace TweetDuck.Core.Utils{
browser.FrameLoadStart += (sender, args) => { browser.FrameLoadStart += (sender, args) => {
if (args.Frame.IsMain && Config.ZoomLevel != 100){ if (args.Frame.IsMain && Config.ZoomLevel != 100){
SetZoomLevel(args.Browser, Config.ZoomLevel); SetZoomLevel(args.Browser.GetHost(), Config.ZoomLevel);
} }
}; };
} }
private const string TwitterTrackingUrl = "t.co"; public static void OpenDevToolsCustom(this IWebBrowser browser){
var info = new WindowInfo();
info.SetAsPopup(IntPtr.Zero, "Dev Tools");
public enum UrlCheckResult{ if (Config.DevToolsWindowOnTop){
Invalid, Tracking, Fine info.ExStyle |= 0x00000008; // WS_EX_TOPMOST
}
public static UrlCheckResult CheckUrl(string url){
if (Uri.TryCreate(url, UriKind.Absolute, out Uri uri)){
string scheme = uri.Scheme;
if (scheme == Uri.UriSchemeHttps || scheme == Uri.UriSchemeHttp || scheme == Uri.UriSchemeFtp || scheme == Uri.UriSchemeMailto){
return uri.Host == TwitterTrackingUrl ? UrlCheckResult.Tracking : UrlCheckResult.Fine;
}
} }
return UrlCheckResult.Invalid; browser.GetBrowserHost().ShowDevTools(info);
} }
public static void OpenExternalBrowser(string url){ public static void OpenExternalBrowser(string url){
if (string.IsNullOrWhiteSpace(url))return; if (string.IsNullOrWhiteSpace(url)){
return;
}
switch(CheckUrl(url)){ switch(TwitterUrls.Check(url)){
case UrlCheckResult.Fine: case TwitterUrls.UrlType.Fine:
if (FormGuide.CheckGuideUrl(url, out string hash)){ if (FormGuide.CheckGuideUrl(url, out string hash)){
FormGuide.Show(hash); FormGuide.Show(hash);
} }
@@ -107,8 +106,11 @@ namespace TweetDuck.Core.Utils{
WindowsUtils.OpenAssociatedProgram(url); WindowsUtils.OpenAssociatedProgram(url);
} }
else{ else{
string quotedUrl = '"' + url + '"';
string browserArgs = Config.BrowserPathArgs == null ? quotedUrl : Config.BrowserPathArgs + ' ' + quotedUrl;
try{ try{
using(Process.Start(browserPath, url)){} using(Process.Start(browserPath, browserArgs)){}
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Error Opening Browser", "Could not open the browser.", true, e); Program.Reporter.HandleException("Error Opening Browser", "Could not open the browser.", true, e);
} }
@@ -117,12 +119,12 @@ namespace TweetDuck.Core.Utils{
break; break;
case UrlCheckResult.Tracking: case TwitterUrls.UrlType.Tracking:
if (Config.IgnoreTrackingUrlWarning){ if (Config.IgnoreTrackingUrlWarning){
goto case UrlCheckResult.Fine; goto case TwitterUrls.UrlType.Fine;
} }
using(FormMessage form = new FormMessage("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, MessageBoxIcon.Warning)){ using(FormMessage form = new FormMessage("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n" + url, MessageBoxIcon.Warning)){
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel | ControlType.Focused); form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel | ControlType.Focused);
form.AddButton(FormMessage.Yes, DialogResult.Yes, ControlType.Accept); form.AddButton(FormMessage.Yes, DialogResult.Yes, ControlType.Accept);
form.AddButton("Always Visit", DialogResult.Ignore); form.AddButton("Always Visit", DialogResult.Ignore);
@@ -135,20 +137,22 @@ namespace TweetDuck.Core.Utils{
} }
if (result == DialogResult.Ignore || result == DialogResult.Yes){ if (result == DialogResult.Ignore || result == DialogResult.Yes){
goto case UrlCheckResult.Fine; goto case TwitterUrls.UrlType.Fine;
} }
} }
break; break;
case UrlCheckResult.Invalid: case TwitterUrls.UrlType.Invalid:
FormMessage.Warning("Blocked URL", "A potentially malicious URL was blocked from opening:\n"+url, FormMessage.OK); FormMessage.Warning("Blocked URL", "A potentially malicious or invalid URL was blocked from opening:\n" + url, FormMessage.OK);
break; break;
} }
} }
public static void OpenExternalSearch(string query){ public static void OpenExternalSearch(string query){
if (string.IsNullOrWhiteSpace(query))return; if (string.IsNullOrWhiteSpace(query)){
return;
}
string searchUrl = Config.SearchEngineUrl; string searchUrl = Config.SearchEngineUrl;
@@ -170,60 +174,12 @@ namespace TweetDuck.Core.Utils{
} }
} }
else{ else{
OpenExternalBrowser(searchUrl+Uri.EscapeUriString(query)); OpenExternalBrowser(searchUrl + Uri.EscapeUriString(query));
} }
} }
public static string GetFileNameFromUrl(string url){
string file = Path.GetFileName(new Uri(url).AbsolutePath);
return string.IsNullOrEmpty(file) ? null : file;
}
public static string GetErrorName(CefErrorCode code){
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
}
public static WebClient CreateWebClient(){
WindowsUtils.EnsureTLS12();
WebClient client = new WebClient{ Proxy = null };
client.Headers[HttpRequestHeader.UserAgent] = UserAgentVanilla;
return client;
}
public static WebClient DownloadFileAsync(string url, string target, string cookie, Action onSuccess, Action<Exception> onFailure){
WebClient client = CreateWebClient();
if (cookie != null){
client.Headers[HttpRequestHeader.Cookie] = cookie;
}
client.DownloadFileCompleted += (sender, args) => {
if (args.Cancelled){
try{
File.Delete(target);
}catch{
// didn't want it deleted anyways
}
}
else if (args.Error != null){
onFailure?.Invoke(args.Error);
}
else{
onSuccess?.Invoke();
}
};
client.DownloadFileAsync(new Uri(url), target);
return client;
}
public static int Scale(int baseValue, double scaleFactor){ public static int Scale(int baseValue, double scaleFactor){
return (int)Math.Round(baseValue*scaleFactor); return (int)Math.Round(baseValue * scaleFactor);
}
public static void SetZoomLevel(IBrowser browser, int percentage){
browser.GetHost().SetZoomLevel(Math.Log(percentage/100.0, 1.2));
} }
} }
} }

View File

@@ -133,7 +133,7 @@ namespace TweetDuck.Core.Utils{
ticks = (uint)Environment.TickCount; ticks = (uint)Environment.TickCount;
} }
int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks-info.dwTime).TotalSeconds); int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks - info.dwTime).TotalSeconds);
return Math.Max(0, seconds); // ignore rollover after several weeks of uptime return Math.Max(0, seconds); // ignore rollover after several weeks of uptime
} }

View File

@@ -2,100 +2,50 @@
using CefSharp; using CefSharp;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Data; using TweetDuck.Data;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using TweetLib.Core.Features.Twitter;
using TweetLib.Core.Utils;
using Cookie = CefSharp.Cookie; 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 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;margin:0}';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"));
private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)/?$", RegexOptions.Compiled), false);
public static Regex RegexAccount => RegexAccountLazy.Value;
public static readonly string[] DictionaryWords = { public static readonly string[] DictionaryWords = {
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD" "tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
}; };
public static readonly string[] ValidImageExtensions = {
".jpg", ".jpeg", ".png", ".gif"
};
public enum ImageQuality{
Default, Orig
}
public static bool IsTweetDeckWebsite(IFrame frame){
return frame.Url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitterWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/");
}
public static bool IsTwitterLogin2FactorWebsite(IFrame frame){
return frame.Url.Contains("//twitter.com/account/login_verification");
}
private static string ExtractMediaBaseLink(string url){
int slash = url.LastIndexOf('/');
return slash == -1 ? url : StringUtils.ExtractBefore(url, ':', slash);
}
public static string GetMediaLink(string url, ImageQuality quality){
if (quality == ImageQuality.Orig){
string result = ExtractMediaBaseLink(url);
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";
}
return result;
}
else{
return url;
}
}
public static string GetImageFileName(string url){
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
}
public static void ViewImage(string url, ImageQuality quality){ public static void ViewImage(string url, ImageQuality quality){
void ViewImageInternal(string path){ static void ViewImageInternal(string path){
string ext = Path.GetExtension(path); string ext = Path.GetExtension(path);
if (ValidImageExtensions.Contains(ext)){ if (ImageUrl.ValidExtensions.Contains(ext)){
WindowsUtils.OpenAssociatedProgram(path); WindowsUtils.OpenAssociatedProgram(path);
} }
else{ else{
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK); FormMessage.Error("Image Download", "Invalid file extension " + ext, FormMessage.OK);
} }
} }
string file = Path.Combine(BrowserCache.CacheFolder, GetImageFileName(url) ?? Path.GetRandomFileName()); string file = Path.Combine(BrowserCache.CacheFolder, TwitterUrls.GetImageFileName(url) ?? Path.GetRandomFileName());
if (WindowsUtils.FileExistsAndNotEmpty(file)){ if (FileUtils.FileExistsAndNotEmpty(file)){
ViewImageInternal(file); ViewImageInternal(file);
} }
else{ else{
DownloadFileAuth(GetMediaLink(url, quality), file, () => { DownloadFileAuth(TwitterUrls.GetMediaLink(url, quality), file, () => {
ViewImageInternal(file); ViewImageInternal(file);
}, ex => { }, ex => {
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); FormMessage.Error("Image Download", "An error occurred while downloading the image: " + ex.Message, FormMessage.OK);
}); });
} }
} }
@@ -109,22 +59,22 @@ namespace TweetDuck.Core.Utils{
return; return;
} }
string firstImageLink = GetMediaLink(urls[0], quality); string firstImageLink = TwitterUrls.GetMediaLink(urls[0], quality);
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/')); int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
string filename = GetImageFileName(firstImageLink); string filename = TwitterUrls.GetImageFileName(firstImageLink);
string ext = Path.GetExtension(filename); // includes dot string ext = Path.GetExtension(filename); // includes dot
using(SaveFileDialog dialog = new SaveFileDialog{ using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
OverwritePrompt = urls.Length == 1, OverwritePrompt = urls.Length == 1,
Title = "Save Image", Title = "Save Image",
FileName = qualityIndex == -1 ? filename : $"{username} {Path.ChangeExtension(filename, null)} {firstImageLink.Substring(qualityIndex+1)}".Trim()+ext, FileName = qualityIndex == -1 ? filename : $"{username} {Path.ChangeExtension(filename, null)} {firstImageLink.Substring(qualityIndex + 1)}".Trim() + ext,
Filter = (urls.Length == 1 ? "Image" : "Images")+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") Filter = (urls.Length == 1 ? "Image" : "Images") + (string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
void OnFailure(Exception ex){ static void OnFailure(Exception ex){
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK); FormMessage.Error("Image Download", "An error occurred while downloading the image: " + ex.Message, FormMessage.OK);
} }
if (urls.Length == 1){ if (urls.Length == 1){
@@ -135,7 +85,7 @@ namespace TweetDuck.Core.Utils{
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++){
DownloadFileAuth(GetMediaLink(urls[index], quality), $"{pathBase} {index+1}{pathExt}", null, OnFailure); DownloadFileAuth(TwitterUrls.GetMediaLink(urls[index], quality), $"{pathBase} {index + 1}{pathExt}", null, OnFailure);
} }
} }
} }
@@ -143,7 +93,7 @@ namespace TweetDuck.Core.Utils{
} }
public static void DownloadVideo(string url, string username){ public static void DownloadVideo(string url, string username){
string filename = BrowserUtils.GetFileNameFromUrl(url); string filename = TwitterUrls.GetFileNameFromUrl(url);
string ext = Path.GetExtension(filename); string ext = Path.GetExtension(filename);
using(SaveFileDialog dialog = new SaveFileDialog{ using(SaveFileDialog dialog = new SaveFileDialog{
@@ -151,11 +101,11 @@ namespace TweetDuck.Core.Utils{
OverwritePrompt = true, OverwritePrompt = true,
Title = "Save Video", Title = "Save Video",
FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}".TrimStart(), FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}".TrimStart(),
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){
DownloadFileAuth(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);
}); });
} }
} }
@@ -178,7 +128,10 @@ namespace TweetDuck.Core.Utils{
} }
} }
BrowserUtils.DownloadFileAsync(url, target, cookieStr, onSuccess, onFailure); WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentChrome);
client.Headers[HttpRequestHeader.Cookie] = cookieStr;
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(target, onSuccess, onFailure);
client.DownloadFileAsync(new Uri(url), target);
}, scheduler); }, scheduler);
} }
} }

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@@ -12,66 +11,17 @@ using Microsoft.Win32;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class WindowsUtils{ static class WindowsUtils{
private static readonly bool IsWindows8OrNewer = OSVersionEquals(major: 6, minor: 2); // windows 8/10
public static bool ShouldAvoidToolWindow { get; } = IsWindows8OrNewer;
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false); private static readonly Lazy<Regex> RegexStripHtmlStyles = new Lazy<Regex>(() => new Regex(@"\s?(?:style|class)="".*?"""), false);
private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false); private static readonly Lazy<Regex> RegexOffsetClipboardHtml = new Lazy<Regex>(() => new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)"), false);
private static readonly bool IsWindows8OrNewer; private static bool OSVersionEquals(int major, int minor){
private static bool HasMicrosoftBeenBroughtTo2008Yet;
public static int CurrentProcessID { get; }
public static bool ShouldAvoidToolWindow { get; }
public static bool IsAeroEnabled => IsWindows8OrNewer || (NativeMethods.DwmIsCompositionEnabled(out bool isCompositionEnabled) == 0 && isCompositionEnabled);
static WindowsUtils(){
using(Process me = Process.GetCurrentProcess()){
CurrentProcessID = me.Id;
}
Version ver = Environment.OSVersion.Version; Version ver = Environment.OSVersion.Version;
IsWindows8OrNewer = ver.Major == 6 && ver.Minor == 2; // windows 8/10 return ver.Major == major && ver.Minor == minor;
ShouldAvoidToolWindow = IsWindows8OrNewer;
}
public static void EnsureTLS12(){
if (!HasMicrosoftBeenBroughtTo2008Yet){
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
HasMicrosoftBeenBroughtTo2008Yet = true;
}
}
public static void CreateDirectoryForFile(string file){
string dir = Path.GetDirectoryName(file);
if (dir == null){
throw new ArgumentException("Invalid file path: "+file);
}
else if (dir.Length > 0){
Directory.CreateDirectory(dir);
}
}
public static bool CheckFolderWritePermission(string path){
string testFile = Path.Combine(path, ".test");
try{
Directory.CreateDirectory(path);
using(File.Create(testFile)){}
File.Delete(testFile);
return true;
}catch{
return false;
}
}
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){
@@ -87,7 +37,7 @@ namespace TweetDuck.Core.Utils{
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user }catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
return false; return false;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for "+file, true, e); Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for " + file, true, e);
return false; return false;
} }
} }
@@ -129,8 +79,8 @@ namespace TweetDuck.Core.Utils{
string updatedHtml = RegexStripHtmlStyles.Value.Replace(originalHtml, string.Empty); string updatedHtml = RegexStripHtmlStyles.Value.Replace(originalHtml, string.Empty);
int removed = originalHtml.Length-updatedHtml.Length; int removed = originalHtml.Length - updatedHtml.Length;
updatedHtml = RegexOffsetClipboardHtml.Value.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0')); updatedHtml = RegexOffsetClipboardHtml.Value.Replace(updatedHtml, match => (int.Parse(match.Value) - removed).ToString().PadLeft(match.Value.Length, '0'));
DataObject obj = new DataObject(); DataObject obj = new DataObject();
obj.SetText(originalText, TextDataFormat.UnicodeText); obj.SetText(originalText, TextDataFormat.UnicodeText);
@@ -157,34 +107,34 @@ namespace TweetDuck.Core.Utils{
} }
public static IEnumerable<Browser> FindInstalledBrowsers(){ public static IEnumerable<Browser> FindInstalledBrowsers(){
IEnumerable<Browser> ReadBrowsersFromKey(RegistryHive hive){ static IEnumerable<Browser> ReadBrowsersFromKey(RegistryHive hive){
using(RegistryKey root = RegistryKey.OpenBaseKey(hive, RegistryView.Default)) using RegistryKey root = RegistryKey.OpenBaseKey(hive, RegistryView.Default);
using(RegistryKey browserList = root.OpenSubKey(@"SOFTWARE\Clients\StartMenuInternet", false)){ using RegistryKey browserList = root.OpenSubKey(@"SOFTWARE\Clients\StartMenuInternet", false);
if (browserList == null){
yield break; if (browserList == null){
yield break;
}
foreach(string sub in browserList.GetSubKeyNames()){
using RegistryKey browserKey = browserList.OpenSubKey(sub, false);
using RegistryKey shellKey = browserKey?.OpenSubKey(@"shell\open\command");
if (shellKey == null){
continue;
} }
foreach(string sub in browserList.GetSubKeyNames()){ string browserName = browserKey.GetValue(null) as string;
using(RegistryKey browserKey = browserList.OpenSubKey(sub, false)) string browserPath = shellKey.GetValue(null) as string;
using(RegistryKey shellKey = browserKey?.OpenSubKey(@"shell\open\command")){
if (shellKey == null){
continue;
}
string browserName = browserKey.GetValue(null) as string; if (string.IsNullOrEmpty(browserName) || string.IsNullOrEmpty(browserPath)){
string browserPath = shellKey.GetValue(null) as string; continue;
if (string.IsNullOrEmpty(browserName) || string.IsNullOrEmpty(browserPath)){
continue;
}
if (browserPath[0] == '"' && browserPath[browserPath.Length-1] == '"'){
browserPath = browserPath.Substring(1, browserPath.Length-2);
}
yield return new Browser(browserName, browserPath);
}
} }
if (browserPath[0] == '"' && browserPath[browserPath.Length - 1] == '"'){
browserPath = browserPath.Substring(1, browserPath.Length - 2);
}
yield return new Browser(browserName, browserPath);
} }
} }

View File

@@ -1,8 +0,0 @@
using System;
namespace TweetDuck.Data.Serialization{
interface ITypeConverter{
bool TryWriteType(Type type, object value, out string converted);
bool TryReadType(Type type, string value, out object converted);
}
}

View File

@@ -1,8 +1,8 @@
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetLib.Core.Serialization.Converters;
using TweetDuck.Data.Serialization; using TweetLib.Core.Utils;
namespace TweetDuck.Data{ namespace TweetDuck.Data{
sealed class WindowState{ sealed class WindowState{

58
Impl/LockHandler.cs Normal file
View File

@@ -0,0 +1,58 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using TweetDuck.Core.Utils;
using TweetLib.Core.Application;
namespace TweetDuck.Impl{
class LockHandler : IAppLockHandler{
private const int WaitRetryDelay = 250;
private const int RestoreFailTimeout = 2000;
private const int CloseNaturallyTimeout = 10000;
private const int CloseKillTimeout = 5000;
bool IAppLockHandler.RestoreProcess(Process process){
if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
return true;
}
}
return false;
}
bool IAppLockHandler.CloseProcess(Process process){
try{
if (process.CloseMainWindow()){
// ReSharper disable once AccessToDisposedClosure
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
}
if (!process.HasExited){
process.Kill();
// ReSharper disable once AccessToDisposedClosure
WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
}
if (process.HasExited){
process.Dispose();
return true;
}
else{
return false;
}
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
bool hasExited = CheckProcessExited(process);
process.Dispose();
return hasExited;
}
}
private static bool CheckProcessExited(Process process){
process.Refresh();
return process.HasExited;
}
}
}

16
Impl/SystemHandler.cs Normal file
View File

@@ -0,0 +1,16 @@
using System.Diagnostics;
using System.IO;
using TweetLib.Core.Application;
namespace TweetDuck.Impl{
class SystemHandler : IAppSystemHandler{
void IAppSystemHandler.OpenFileExplorer(string path){
if (File.Exists(path)){
using(Process.Start("explorer.exe", "/select,\"" + path.Replace('/', '\\') + "\"")){}
}
else if (Directory.Exists(path)){
using(Process.Start("explorer.exe", '"' + path.Replace('/', '\\') + '"')){}
}
}
}
}

View File

@@ -1,88 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace TweetDuck.Plugins.Enums{
[Flags]
enum PluginEnvironment{
None = 0,
Browser = 1,
Notification = 2
}
static class PluginEnvironmentExtensions{
public static IEnumerable<PluginEnvironment> Values{
get{
yield return PluginEnvironment.Browser;
yield return PluginEnvironment.Notification;
}
}
public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
return environment == PluginEnvironment.Browser;
}
public static string GetPluginScriptFile(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.Browser: return "browser.js";
case PluginEnvironment.Notification: return "notification.js";
default: return null;
}
}
public static string GetPluginScriptVariables(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.Browser: return "$,$TD,$TDP,TD";
case PluginEnvironment.Notification: return "$TD,$TDP";
default: return string.Empty;
}
}
public static IReadOnlyDictionary<PluginEnvironment, T> Map<T>(T forNone, T forBrowser, T forNotification){
return new PluginEnvironmentDictionary<T>(forNone, forBrowser, forNotification);
}
[SuppressMessage("ReSharper", "MemberHidesStaticFromOuterClass")]
private sealed class PluginEnvironmentDictionary<T> : IReadOnlyDictionary<PluginEnvironment, T>{
private const int TotalKeys = 3;
public IEnumerable<PluginEnvironment> Keys => Enum.GetValues(typeof(PluginEnvironment)).Cast<PluginEnvironment>();
public IEnumerable<T> Values => data;
public int Count => TotalKeys;
public T this[PluginEnvironment key] => data[(int)key];
private readonly T[] data;
public PluginEnvironmentDictionary(T forNone, T forBrowser, T forNotification){
this.data = new T[TotalKeys];
this.data[(int)PluginEnvironment.None] = forNone;
this.data[(int)PluginEnvironment.Browser] = forBrowser;
this.data[(int)PluginEnvironment.Notification] = forNotification;
}
public bool ContainsKey(PluginEnvironment key){
return key >= 0 && (int)key < TotalKeys;
}
public bool TryGetValue(PluginEnvironment key, out T value){
if (ContainsKey(key)){
value = this[key];
return true;
}
else{
value = default(T);
return false;
}
}
public IEnumerator<KeyValuePair<PluginEnvironment, T>> GetEnumerator(){
return Keys.Select(key => new KeyValuePair<PluginEnvironment, T>(key, this[key])).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
}

View File

@@ -1,5 +0,0 @@
namespace TweetDuck.Plugins.Enums{
enum PluginFolder{
Root, Data
}
}

View File

@@ -1,23 +0,0 @@
namespace TweetDuck.Plugins.Enums{
enum PluginGroup{
Official, Custom
}
static class PluginGroupExtensions{
public static string GetIdentifierPrefix(this PluginGroup group){
switch(group){
case PluginGroup.Official: return "official/";
case PluginGroup.Custom: return "custom/";
default: return "unknown/";
}
}
public static string GetIdentifierPrefixShort(this PluginGroup group){
switch(group){
case PluginGroup.Official: return "o/";
case PluginGroup.Custom: return "c/";
default: return "?/";
}
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
namespace TweetDuck.Plugins{
sealed class PluginBridge{
private static string SanitizeCacheKey(string key){
return key.Replace('\\', '/').Trim();
}
private readonly PluginManager manager;
private readonly TwoKeyDictionary<int, string, string> fileCache = new TwoKeyDictionary<int, string, string>(4, 2);
private readonly TwoKeyDictionary<int, string, InjectedHTML> notificationInjections = new TwoKeyDictionary<int, string, InjectedHTML>(4, 1);
public IEnumerable<InjectedHTML> NotificationInjections => notificationInjections.InnerValues;
public HashSet<Plugin> WithConfigureFunction { get; } = new HashSet<Plugin>();
public PluginBridge(PluginManager manager){
this.manager = manager;
this.manager.Reloaded += manager_Reloaded;
this.manager.Config.PluginChangedState += Config_PluginChangedState;
}
// Event handlers
private void manager_Reloaded(object sender, PluginErrorEventArgs e){
fileCache.Clear();
}
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
if (!e.IsEnabled){
int token = manager.GetTokenFromPlugin(e.Plugin);
fileCache.Remove(token);
notificationInjections.Remove(token);
}
}
// Utility methods
private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
Plugin plugin = manager.GetPluginFromToken(token);
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path);
if (fullPath.Length == 0){
switch(folder){
case PluginFolder.Data: throw new ArgumentException("File path has to be relative to the plugin data folder.");
case PluginFolder.Root: throw new ArgumentException("File path has to be relative to the plugin root folder.");
default: throw new ArgumentException("Invalid folder type "+folder+", this is a TweetDuck error.");
}
}
else{
return fullPath;
}
}
private string ReadFileUnsafe(int token, string cacheKey, string fullPath, bool readCached){
cacheKey = SanitizeCacheKey(cacheKey);
if (readCached && fileCache.TryGetValue(token, cacheKey, out string cachedContents)){
return cachedContents;
}
try{
return fileCache[token, cacheKey] = File.ReadAllText(fullPath, Encoding.UTF8);
}catch(FileNotFoundException){
throw new FileNotFoundException("File not found.");
}catch(DirectoryNotFoundException){
throw new DirectoryNotFoundException("Directory not found.");
}
}
// Public methods
public void WriteFile(int token, string path, string contents){
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
WindowsUtils.CreateDirectoryForFile(fullPath);
File.WriteAllText(fullPath, contents, Encoding.UTF8);
fileCache[token, SanitizeCacheKey(path)] = contents;
}
public string ReadFile(int token, string path, bool cache){
return ReadFileUnsafe(token, path, GetFullPathOrThrow(token, PluginFolder.Data, path), cache);
}
public void DeleteFile(int token, string path){
string fullPath = GetFullPathOrThrow(token, PluginFolder.Data, path);
fileCache.Remove(token, SanitizeCacheKey(path));
File.Delete(fullPath);
}
public bool CheckFileExists(int token, string path){
return File.Exists(GetFullPathOrThrow(token, PluginFolder.Data, path));
}
public string ReadFileRoot(int token, string path){
return ReadFileUnsafe(token, "root*"+path, GetFullPathOrThrow(token, PluginFolder.Root, path), true);
}
public bool CheckFileExistsRoot(int token, string path){
return File.Exists(GetFullPathOrThrow(token, PluginFolder.Root, path));
}
public void InjectIntoNotificationsBefore(int token, string key, string search, string html){
notificationInjections[token, key] = new InjectedHTML(InjectedHTML.Position.Before, search, html);
}
public void InjectIntoNotificationsAfter(int token, string key, string search, string html){
notificationInjections[token, key] = new InjectedHTML(InjectedHTML.Position.After, search, html);
}
public void SetConfigurable(int token){
Plugin plugin = manager.GetPluginFromToken(token);
if (plugin != null){
WithConfigureFunction.Add(plugin);
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace TweetDuck.Plugins.Controls { namespace TweetDuck.Plugins {
partial class PluginControl { partial class PluginControl {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.

View File

@@ -3,9 +3,10 @@ using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins.Enums; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Plugins.Controls{ namespace TweetDuck.Plugins{
sealed partial class PluginControl : UserControl{ sealed partial class PluginControl : UserControl{
private readonly PluginManager pluginManager; private readonly PluginManager pluginManager;
private readonly Plugin plugin; private readonly Plugin plugin;
@@ -26,7 +27,7 @@ namespace TweetDuck.Plugins.Controls{
float dpiScale = this.GetDPIScale(); float dpiScale = this.GetDPIScale();
if (dpiScale > 1F){ if (dpiScale > 1F){
Size = MaximumSize = new Size(MaximumSize.Width, MaximumSize.Height+3); Size = MaximumSize = new Size(MaximumSize.Width, MaximumSize.Height + 3);
} }
this.labelName.Text = plugin.Name; this.labelName.Text = plugin.Name;
@@ -55,19 +56,19 @@ namespace TweetDuck.Plugins.Controls{
private void panelDescription_Resize(object sender, EventArgs e){ private void panelDescription_Resize(object sender, EventArgs e){
SuspendLayout(); SuspendLayout();
int maxWidth = panelDescription.Width-(panelDescription.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0); int maxWidth = panelDescription.Width - (panelDescription.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0);
labelDescription.MaximumSize = new Size(maxWidth, int.MaxValue); labelDescription.MaximumSize = new Size(maxWidth, int.MaxValue);
Font font = labelDescription.Font; Font font = labelDescription.Font;
int descriptionLines = TextRenderer.MeasureText(labelDescription.Text, font, new Size(maxWidth, int.MaxValue), TextFormatFlags.WordBreak).Height/(font.Height-1); int descriptionLines = TextRenderer.MeasureText(labelDescription.Text, font, new Size(maxWidth, int.MaxValue), TextFormatFlags.WordBreak).Height / (font.Height - 1);
int requiredLines = Math.Max(descriptionLines, 1+(string.IsNullOrEmpty(labelVersion.Text) ? 0 : 1)+(isConfigurable ? 1 : 0)); int requiredLines = Math.Max(descriptionLines, 1 + (string.IsNullOrEmpty(labelVersion.Text) ? 0 : 1) + (isConfigurable ? 1 : 0));
switch(requiredLines){ nextHeight = requiredLines switch{
case 1: nextHeight = MaximumSize.Height-2*(font.Height-1); break; 1 => MaximumSize.Height - 2 * (font.Height - 1),
case 2: nextHeight = MaximumSize.Height-(font.Height-1); break; 2 => MaximumSize.Height - 1 * (font.Height - 1),
default: nextHeight = MaximumSize.Height; break; _ => MaximumSize.Height
} };
if (nextHeight != Height){ if (nextHeight != Height){
timerLayout.Start(); timerLayout.Start();

View File

@@ -0,0 +1,34 @@
using System;
using CefSharp;
using TweetDuck.Core.Adapters;
using TweetLib.Core.Browser;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Events;
using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Plugins{
sealed class PluginDispatcher : IPluginDispatcher{
public event EventHandler<PluginDispatchEventArgs> Ready;
private readonly IWebBrowser browser;
private readonly IScriptExecutor executor;
public PluginDispatcher(IWebBrowser browser){
this.browser = browser;
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
this.executor = new CefScriptExecutor(browser);
}
void IPluginDispatcher.AttachBridge(string name, object bridge){
browser.RegisterAsyncJsObject(name, bridge);
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
IFrame frame = e.Frame;
if (frame.IsMain && TwitterUrls.IsTweetDeck(frame.Url)){
Ready?.Invoke(this, new PluginDispatchEventArgs(executor));
}
}
}
}

View File

@@ -1,70 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins{
static class PluginLoader{
private static readonly string[] EndTag = { "[END]" };
public static Plugin FromFolder(string path, PluginGroup group){
string name = Path.GetFileName(path);
if (string.IsNullOrEmpty(name)){
throw new ArgumentException("Could not extract directory name from path: "+path);
}
Plugin.Builder builder = new Plugin.Builder(group, name, path, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name));
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
builder.AddEnvironment(PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)));
}
string metaFile = Path.Combine(path, ".meta");
if (!File.Exists(metaFile)){
throw new ArgumentException("Plugin is missing a .meta file");
}
string currentTag = null, currentContents = string.Empty;
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
if (line[0] == '[' && line[line.Length-1] == ']'){
if (currentTag != null){
SetProperty(builder, currentTag, currentContents);
}
currentTag = line.Substring(1, line.Length-2).ToUpper();
currentContents = string.Empty;
if (line.Equals(EndTag[0])){
break;
}
}
else if (currentTag != null){
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
}
else{
throw new FormatException("Missing metadata tag before value: "+line);
}
}
return builder.BuildAndSetup();
}
private static void SetProperty(Plugin.Builder builder, string tag, string value){
switch(tag){
case "NAME": builder.Name = value; break;
case "DESCRIPTION": builder.Description = value; break;
case "AUTHOR": builder.Author = value; break;
case "VERSION": builder.Version = value; break;
case "WEBSITE": builder.Website = value; break;
case "CONFIGFILE": builder.ConfigFile = value; break;
case "CONFIGDEFAULT": builder.ConfigDefault = value; break;
case "REQUIRES": builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException("Invalid required minimum version: "+value); break;
default: throw new FormatException("Invalid metadata tag: "+tag);
}
}
}
}

View File

@@ -1,183 +0,0 @@
using CefSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Core.Utils;
using TweetDuck.Data;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
namespace TweetDuck.Plugins{
sealed class PluginManager{
private static readonly IReadOnlyDictionary<PluginEnvironment, string> PluginSetupScriptNames = PluginEnvironmentExtensions.Map(null, "plugins.browser.js", "plugins.notification.js");
public string PathOfficialPlugins => Path.Combine(rootPath, "official");
public string PathCustomPlugins => Path.Combine(rootPath, "user");
public IEnumerable<Plugin> Plugins => plugins;
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
public IPluginConfig Config { get; }
public event EventHandler<PluginErrorEventArgs> Reloaded;
public event EventHandler<PluginErrorEventArgs> Executed;
private readonly string rootPath;
private readonly PluginBridge bridge;
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
private readonly Random rand = new Random();
private IWebBrowser mainBrowser;
public PluginManager(IPluginConfig config, string rootPath){
this.Config = config;
this.Config.PluginChangedState += Config_PluginChangedState;
this.rootPath = rootPath;
this.bridge = new PluginBridge(this);
}
public void Register(IWebBrowser browser, PluginEnvironment environment, Control sync, bool asMainBrowser = false){
browser.FrameLoadEnd += (sender, args) => {
IFrame frame = args.Frame;
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
ExecutePlugins(frame, environment, sync);
}
};
browser.RegisterAsyncJsObject("$TDP", bridge);
if (asMainBrowser){
mainBrowser = browser;
}
}
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
mainBrowser?.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
public bool IsPluginInstalled(string identifier){
return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
}
public bool HasAnyPlugin(PluginEnvironment environment){
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
}
public bool IsPluginConfigurable(Plugin plugin){
return plugin.HasConfig || bridge.WithConfigureFunction.Contains(plugin);
}
public void ConfigurePlugin(Plugin plugin){
if (bridge.WithConfigureFunction.Contains(plugin)){
mainBrowser?.ExecuteScriptAsync("TDPF_configurePlugin", plugin);
}
else if (plugin.HasConfig){
if (File.Exists(plugin.ConfigPath)){
using(Process.Start("explorer.exe", "/select,\""+plugin.ConfigPath.Replace('/', '\\')+"\"")){}
}
else{
using(Process.Start("explorer.exe", '"'+plugin.GetPluginFolder(PluginFolder.Data).Replace('/', '\\')+'"')){}
}
}
}
public int GetTokenFromPlugin(Plugin plugin){
foreach(KeyValuePair<int, Plugin> kvp in tokens){
if (kvp.Value.Equals(plugin)){
return kvp.Key;
}
}
int token, attempts = 1000;
do{
token = rand.Next();
}while(tokens.ContainsKey(token) && --attempts >= 0);
if (attempts < 0){
token = -tokens.Count-1;
}
tokens[token] = plugin;
return token;
}
public Plugin GetPluginFromToken(int token){
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
}
public void Reload(){
plugins.Clear();
tokens.Clear();
List<string> loadErrors = new List<string>(2);
IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
if (!Directory.Exists(path)){
yield break;
}
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
Plugin plugin;
try{
plugin = PluginLoader.FromFolder(fullDir, group);
}catch(Exception e){
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
continue;
}
yield return plugin;
}
}
plugins.UnionWith(LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official));
plugins.UnionWith(LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom));
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
}
private void ExecutePlugins(IFrame frame, PluginEnvironment environment, Control sync){
if (!HasAnyPlugin(environment) || !ScriptLoader.ExecuteFile(frame, PluginSetupScriptNames[environment], sync)){
return;
}
bool includeDisabled = environment.IncludesDisabledPlugins();
if (includeDisabled){
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
}
List<string> failedPlugins = new List<string>(1);
foreach(Plugin plugin in Plugins){
string path = plugin.GetScriptPath(environment);
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun){
continue;
}
string script;
try{
script = File.ReadAllText(path);
}catch(Exception e){
failedPlugins.Add(plugin.Identifier+" ("+Path.GetFileName(path)+"): "+e.Message);
continue;
}
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, GetTokenFromPlugin(plugin), environment), "plugin:"+plugin);
}
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
}
}
}

View File

@@ -2,10 +2,8 @@ using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core; using TweetDuck.Core;
@@ -14,14 +12,19 @@ using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Management; using TweetDuck.Core.Management;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Impl;
using TweetDuck.Resources;
using TweetLib.Core;
using TweetLib.Core.Application.Helpers;
using TweetLib.Core.Collections;
using TweetLib.Core.Utils;
namespace TweetDuck{ namespace TweetDuck{
static class Program{ static class Program{
public const string BrandName = "TweetDuck"; public const string BrandName = Lib.BrandName;
public const string Website = "https://tweetduck.chylex.com"; public const string VersionTag = Lib.VersionTag;
public const string VersionTag = "1.17.4"; public const string Website = "https://tweetduck.chylex.com";
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"));
@@ -48,23 +51,28 @@ namespace TweetDuck{
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
private static bool HasCleanedUp; private static bool HasCleanedUp;
public static CultureInfo Culture { get; }
public static Reporter Reporter { get; } public static Reporter Reporter { get; }
public static ConfigManager Config { get; } public static ConfigManager Config { get; }
public static ScriptLoader Resources { get; }
static Program(){ static Program(){
Culture = CultureInfo.CurrentCulture;
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
#if DEBUG
CultureInfo.DefaultThreadCurrentUICulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us"); // force english exceptions
#endif
Reporter = new Reporter(ErrorLogFilePath); Reporter = new Reporter(ErrorLogFilePath);
Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :("); Reporter.SetupUnhandledExceptionHandler("TweetDuck Has Failed :(");
Config = new ConfigManager(); Config = new ConfigManager();
#if DEBUG
Resources = new ScriptLoaderDebug();
#else
Resources = new ScriptLoader();
#endif
Lib.Initialize(new App.Builder{
ErrorHandler = Reporter,
LockHandler = new LockHandler(),
SystemHandler = new SystemHandler(),
ResourceHandler = Resources
});
} }
[STAThread] [STAThread]
@@ -75,8 +83,8 @@ namespace TweetDuck{
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore"); WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){ if (!FileUtils.CheckFolderWritePermission(StoragePath)){
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK); FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: " + StoragePath, FormMessage.OK);
return; return;
} }
@@ -99,15 +107,17 @@ namespace TweetDuck{
LockManager.Result lockResult = LockManager.Lock(); LockManager.Result lockResult = LockManager.Lock();
if (lockResult == LockManager.Result.HasProcess){ if (lockResult == LockManager.Result.HasProcess){
if (!LockManager.RestoreLockingProcess(2000) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){ if (!LockManager.RestoreLockingProcess() && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
if (!LockManager.CloseLockingProcess(10000, 5000)){ if (!LockManager.CloseLockingProcess()){
FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK); FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
return; return;
} }
lockResult = LockManager.Lock(); lockResult = LockManager.Lock();
} }
else return; else{
return;
}
} }
if (lockResult != LockManager.Result.Success){ if (lockResult != LockManager.Result.Success){
@@ -131,9 +141,9 @@ namespace TweetDuck{
} }
try{ try{
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze, null)); RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
}catch(Exception e){ }catch(Exception e){
FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: "+e.Message, FormMessage.OK); FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message, FormMessage.OK);
return; return;
} }
@@ -144,7 +154,7 @@ namespace TweetDuck{
CefSettings settings = new CefSettings{ CefSettings settings = new CefSettings{
UserAgent = BrowserUtils.UserAgentChrome, UserAgent = BrowserUtils.UserAgentChrome,
BrowserSubprocessPath = BrandName+".Browser.exe", BrowserSubprocessPath = BrandName + ".Browser.exe",
CachePath = StoragePath, CachePath = StoragePath,
UserDataPath = CefDataPath, UserDataPath = CefDataPath,
LogFile = ConsoleLogFilePath, LogFile = ConsoleLogFilePath,
@@ -161,14 +171,15 @@ namespace TweetDuck{
Application.ApplicationExit += (sender, args) => ExitCleanup(); Application.ApplicationExit += (sender, args) => ExitCleanup();
FormBrowser mainForm = new FormBrowser(); FormBrowser mainForm = new FormBrowser();
Resources.Initialize(mainForm);
Application.Run(mainForm); Application.Run(mainForm);
if (mainForm.UpdateInstallerPath != null){ if (mainForm.UpdateInstallerPath != null){
ExitCleanup(); ExitCleanup();
// ProgramPath has a trailing backslash // ProgramPath has a trailing backslash
string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : ""); string updaterArgs = "/SP- /SILENT /FORCECLOSEAPPLICATIONS /UPDATEPATH=\"" + ProgramPath + "\" /RUNARGS=\"" + Arguments.GetCurrentForInstallerCmd() + "\"" + (IsPortable ? " /PORTABLE=1" : "");
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath); bool runElevated = !IsPortable || !FileUtils.CheckFolderWritePermission(ProgramPath);
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){ if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
Application.Exit(); Application.Exit();
@@ -180,14 +191,14 @@ namespace TweetDuck{
} }
private static string GetDataStoragePath(){ private static string GetDataStoragePath(){
string custom = Arguments.GetValue(Arguments.ArgDataFolder, null); string custom = Arguments.GetValue(Arguments.ArgDataFolder);
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){ if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
if (Path.GetInvalidPathChars().Any(custom.Contains)){ if (Path.GetInvalidPathChars().Any(custom.Contains)){
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder contains invalid characters:\n"+custom); Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder contains invalid characters:\n" + custom);
} }
else if (!Path.IsPathRooted(custom)){ else if (!Path.IsPathRooted(custom)){
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n"+custom); Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n" + custom);
} }
return Environment.ExpandEnvironmentVariables(custom); return Environment.ExpandEnvironmentVariables(custom);

View File

@@ -120,26 +120,6 @@ 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>

View File

@@ -136,12 +136,6 @@
<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>

View File

@@ -1,30 +1,28 @@
# Support # Support
[Follow TweetDuck on Twitter](https://twitter.com/TryMyAwesomeApp) &nbsp;|&nbsp; [Support via PayPal](https://paypal.me/chylex) &nbsp;|&nbsp; [Support via Patreon](https://www.patreon.com/chylex) [Follow TweetDuck on Twitter](https://twitter.com/TryMyAwesomeApp) &nbsp;|&nbsp; [Support via Ko-fi](https://ko-fi.com/chylex) &nbsp;|&nbsp; [Support via Patreon](https://www.patreon.com/chylex)
# Build Instructions # Build Instructions
### Setup ### Setup
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): The program can be built using Visual Studio 2019. 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.7.2 SDK
* F# desktop language support * F# desktop language support
* **Desktop development with C++** * **Desktop development with C++**
* *(VS 2017)* VC++ 2017 version 15.9 v14.16 latest v141 tools * MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.20)
* *(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 PM> Install-Package CefSharp.WinForms -Version 67.0.0
PM> Install-Package WindowsAPICodePack-Shell -Version 1.1.1
``` ```
### 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 `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` 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.
### Release ### Release
@@ -44,18 +42,13 @@ If you decide to publicly release a custom version, please make it clear that it
- 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
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)"`
3. Hope the next Visual Studio update fixes it...
### Installers ### Installers
TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin). TweetDuck uses **Inno Setup** for installers and updates. First, download and install [InnoSetup 5.6.1](http://files.jrsoftware.org/is/5/innosetup-5.6.1.exe) (with Preprocessor support) and the [Inno Download Plugin 1.5.0](https://drive.google.com/folderview?id=0Bzw1xBVt0mokSXZrUEFIanV4azA&usp=sharing#list).
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 and Visual Studio for the change to take place.
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! 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 first!
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**

View File

@@ -6,9 +6,11 @@ using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetLib.Core;
using TweetLib.Core.Application;
namespace TweetDuck{ namespace TweetDuck{
sealed class Reporter{ sealed class Reporter : IAppErrorHandler{
private readonly string logFile; private readonly string logFile;
public Reporter(string logFile){ public Reporter(string logFile){
@@ -17,7 +19,7 @@ namespace TweetDuck{
public void SetupUnhandledExceptionHandler(string caption){ public void SetupUnhandledExceptionHandler(string caption){
AppDomain.CurrentDomain.UnhandledException += (sender, args) => { AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
if (args.ExceptionObject is Exception ex) { if (args.ExceptionObject is Exception ex){
HandleException(caption, "An unhandled exception has occurred.", false, ex); HandleException(caption, "An unhandled exception has occurred.", false, ex);
} }
}; };
@@ -28,8 +30,12 @@ namespace TweetDuck{
} }
public bool LogImportant(string data){ public bool LogImportant(string data){
return ((IAppErrorHandler)this).Log(data);
}
bool IAppErrorHandler.Log(string text){
#if DEBUG #if DEBUG
Debug.WriteLine(data); Debug.WriteLine(text);
#endif #endif
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
@@ -38,8 +44,8 @@ namespace TweetDuck{
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n"); build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
} }
build.Append("[").Append(DateTime.Now.ToString("G", Program.Culture)).Append("]\r\n"); build.Append("[").Append(DateTime.Now.ToString("G", Lib.Culture)).Append("]\r\n");
build.Append(data).Append("\r\n\r\n"); build.Append(text).Append("\r\n\r\n");
try{ try{
File.AppendAllText(logFile, build.ToString(), Encoding.UTF8); File.AppendAllText(logFile, build.ToString(), Encoding.UTF8);
@@ -52,8 +58,8 @@ 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 = LogImportant(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);
Button btnExit = form.AddButton(FormMessage.Exit); Button btnExit = form.AddButton(FormMessage.Exit);
Button btnIgnore = form.AddButton(FormMessage.Ignore, DialogResult.Ignore, ControlType.Cancel); Button btnIgnore = form.AddButton(FormMessage.Ignore, DialogResult.Ignore, ControlType.Cancel);
@@ -109,7 +115,7 @@ namespace TweetDuck{
this.details = details; this.details = details;
} }
public override string ToString() => base.ToString()+"\r\n"+details; public override string ToString() => base.ToString() + "\r\n" + details;
} }
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -8,6 +8,7 @@ enabled(){
_theme: "light", _theme: "light",
themeOverride: false, themeOverride: false,
columnWidth: "310px", columnWidth: "310px",
composerWidth: "default",
fontSize: "12px", fontSize: "12px",
hideTweetActions: true, hideTweetActions: true,
moveTweetActionsToRight: true, moveTweetActionsToRight: true,
@@ -380,8 +381,15 @@ enabled(){
this.css.insert("#general_settings .cf { display: none !important }"); this.css.insert("#general_settings .cf { display: none !important }");
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }"); this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }"); this.css.insert(`html[data-td-font] { font-size: ${this.config.fontSize} !important }`);
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }"); this.css.insert(`.avatar { border-radius: ${this.config.avatarRadius}% !important }`);
if (this.config.composerWidth !== "default"){
const width = this.config.composerWidth;
this.css.insert(`.js-app-content.is-open { margin-right: ${width} !important; transform: translateX(${width}) !important }`);
this.css.insert(`#tduck .js-app-content.tduck-is-opening { margin-right: 0 !important }`);
this.css.insert(`.js-drawer { width: ${width} !important; left: -${width} !important }`);
}
let currentTheme = TD.settings.getTheme(); let currentTheme = TD.settings.getTheme();
@@ -570,7 +578,7 @@ ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1
let cols = this.config.columnWidth.slice(1); let cols = this.config.columnWidth.slice(1);
this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important; min-width: 160px }"); this.css.insert(".column { width: calc((100vw - 205px) / "+cols+" - 6px) !important; min-width: 160px }");
this.css.insert(".is-condensed .column { width: calc((100vw - 55px) / "+cols+" - 6px) !important }"); this.css.insert(".is-condensed .column { width: calc((100vw - 65px) / "+cols+" - 6px) !important }");
} }
else{ else{
this.css.insert(".column { width: "+this.config.columnWidth+" !important }"); this.css.insert(".column { width: "+this.config.columnWidth+" !important }");
@@ -638,6 +646,13 @@ ${notificationScrollbarColor ? `
$(".js-dropdown.pos-r").toggleClass("pos-r pos-l"); $(".js-dropdown.pos-r").toggleClass("pos-r pos-l");
} }
}; };
this.uiDrawerActiveEvent = (e, data) => {
return if data.activeDrawer === null || this.config.composerWidth === "default";
const ele = $(".js-app-content").addClass("tduck-is-opening");
setTimeout(() => ele.removeClass("tduck-is-opening"), 250);
};
} }
ready(){ ready(){
@@ -647,6 +662,7 @@ ready(){
// layout events // layout events
$(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent); $(document).on("uiShowActionsMenu", this.uiShowActionsMenuEvent);
$(document).on("uiDrawerActive", this.uiDrawerActiveEvent);
// modal // modal
$("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent); $("[data-action='settings-menu']").on("click", this.onSettingsMenuClickedEvent);
@@ -709,10 +725,12 @@ disabled(){
window.clearTimeout(this.optimizationTimer); window.clearTimeout(this.optimizationTimer);
} }
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
$(window).off("focus", this.onWindowFocusEvent); $(window).off("focus", this.onWindowFocusEvent);
$(window).off("blur", this.onWindowBlurEvent); $(window).off("blur", this.onWindowBlurEvent);
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
$(document).off("uiDrawerActive", this.uiDrawerActiveEvent);
TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo; TD.components.GlobalSettings.prototype.getInfo = this.prevFuncSettingsGetInfo;
TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab; TD.components.GlobalSettings.prototype.switchTab = this.prevFuncSettingsSwitchTab;

View File

@@ -55,6 +55,20 @@
<option disabled></option> <option disabled></option>
</select> </select>
<!-- COMPOSER SIZE -->
<label class="txt-uppercase touch-larger-label">
<b>New Tweet panel</b>
</label>
<select data-td-key="composerWidth">
<option value="default">Default</option>
<option value="270px">Narrow (270px)</option>
<option value="310px">Medium (310px)</option>
<option value="350px">Wide (350px)</option>
<option value="custom-px">Custom</option>
<option value="change-custom-px">Change custom value...</option>
</select>
<!-- FONT SIZE --> <!-- FONT SIZE -->
<label class="txt-uppercase touch-larger-label"> <label class="txt-uppercase touch-larger-label">
@@ -69,10 +83,6 @@
<option value="custom-px">Custom</option> <option value="custom-px">Custom</option>
<option value="change-custom-px">Change custom value...</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
@@ -116,6 +126,10 @@
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
Theme color tweaks Theme color tweaks
</label> </label>
<label class="checkbox">
<input data-td-key="forceArialFont" class="js-theme-checkbox touch-larger-label" type="checkbox">
Use Arial as default font
</label>
<!-- ADVANCED --> <!-- ADVANCED -->
@@ -172,7 +186,7 @@
#edit-design-panel { #edit-design-panel {
width: 693px; width: 693px;
height: 380px; height: 424px;
background-color: #FFF; background-color: #FFF;
box-shadow: 0 0 10px rgba(17, 17, 17, 0.5); box-shadow: 0 0 10px rgba(17, 17, 17, 0.5);
} }
@@ -217,7 +231,7 @@
#edit-design-panel-content label.radio { #edit-design-panel-content label.radio {
display: inline-block; display: inline-block;
margin: 0 16px 5px 4px; margin: 0 16px 0 4px;
cursor: pointer; cursor: pointer;
} }

View File

@@ -506,18 +506,6 @@ html.dark .lst-group .selected a:hover{background:#55acee}
html.dark .lst-group .selected .fullname,html.dark .lst-group .selected .inner strong,html.dark .lst-group .selected .list-link,html.dark .lst-group .selected .list-twitter-list,html.dark .lst-group .selected .list-subtitle,html.dark .lst-group .selected .list-account,html.dark .lst-group .selected .list-listmember,html.dark .lst-group .selected .txt-ellipsis{color:#F5F8FA} html.dark .lst-group .selected .fullname,html.dark .lst-group .selected .inner strong,html.dark .lst-group .selected .list-link,html.dark .lst-group .selected .list-twitter-list,html.dark .lst-group .selected .list-subtitle,html.dark .lst-group .selected .list-account,html.dark .lst-group .selected .list-listmember,html.dark .lst-group .selected .txt-ellipsis{color:#F5F8FA}
html.dark .lst-group .selected .username,html.dark .lst-group .selected .bytext,html.dark .lst-group .selected .subtitle,html.dark .lst-group .selected .icon-protected{color:#eef3f7} html.dark .lst-group .selected .username,html.dark .lst-group .selected .bytext,html.dark .lst-group .selected .subtitle,html.dark .lst-group .selected .icon-protected{color:#eef3f7}
html.dark .itm-remove{border-top:1px solid #ddd} html.dark .itm-remove{border-top:1px solid #ddd}
html.dark .caret-outer{border-bottom:7px solid rgba(17,17,17,0.1)}
html.dark .caret-inner{border-bottom:6px solid #fff}
html.dark .drp-h-divider{border-bottom:1px solid #ddd}
html.dark .dropdown-menu .typeahead-item,html.dark .dropdown-menu [data-action]{color:#292F33}
html.dark .dropdown-menu .is-selected{background:#55acee;color:#fff}
html.dark .dropdown-menu .is-selected [data-action]{color:#fff}
html.dark .dropdown-menu .is-selected a:not(:hover):not(:focus){color:#fff}
html.dark .dropdown-menu a:not(:hover):not(:focus){color:#292F33}
html.dark .dropdown-menu-old li:hover{background:#55acee}
html.dark .dropdown-menu-old li:hover a{color:#fff}
html.dark .dropdown-menu-old li:hover .attribution{color:#fff}
html.dark .non-selectable-item{color:#292F33}
html.dark .update-available-item:before{background-color:#FFAD1F} html.dark .update-available-item:before{background-color:#FFAD1F}
html.dark .is-selected .update-available-item:before{background-color:rgba(41,47,51,0.2)} html.dark .is-selected .update-available-item:before{background-color:rgba(41,47,51,0.2)}
html.dark .popover{background-color:#fff;box-shadow:0 0 10px rgba(17,17,17,0.7)} html.dark .popover{background-color:#fff;box-shadow:0 0 10px rgba(17,17,17,0.7)}

View File

@@ -380,13 +380,9 @@ enabled(){
hideKeyboard(); hideKeyboard();
}; };
this.drawerToggleEvent = function(e, data){ this.composerActiveEvent = function(e){
if (data.activeDrawer === "compose"){ $(".emoji-keyboard-popup-btn", me.composeDrawer).on("click", me.emojiKeyboardButtonClickEvent);
setTimeout(function(){ $(".js-docked-compose .js-compose-scroller > .scroll-v", me.composeDrawer).on("scroll", me.composerScrollEvent);
$(".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){
@@ -414,7 +410,7 @@ enabled(){
if (maybeDockedComposePanel.length){ if (maybeDockedComposePanel.length){
maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML); maybeDockedComposePanel.find(".cf.margin-t--12.margin-b--30").first().append(buttonHTML);
this.drawerToggleEvent({}, { activeDrawer: "compose" }); this.composerActiveEvent();
} }
} }
@@ -424,7 +420,7 @@ ready(){
$(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("tduckOldComposerActive", this.composerActiveEvent);
$(document).on("uiComposeImageAdded", this.uploadFilesEvent); $(document).on("uiComposeImageAdded", this.uploadFilesEvent);
this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent); this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
@@ -550,7 +546,7 @@ disabled(){
$(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("tduckOldComposerActive", this.composerActiveEvent);
$(document).off("uiComposeImageAdded", this.uploadFilesEvent); $(document).off("uiComposeImageAdded", this.uploadFilesEvent);
this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent); this.composeDrawer.off("uiComposeTweetSending", this.composerSendingEvent);

View File

@@ -376,7 +376,7 @@ enabled(){
}; };
this.drawerToggleEvent = function(e, data){ this.drawerToggleEvent = function(e, data){
if (data.activeDrawer === null){ if (typeof data === "undefined" || data.activeDrawer !== "compose"){
hideTemplateModal(); hideTemplateModal();
} }
}; };
@@ -385,6 +385,7 @@ enabled(){
ready(){ ready(){
$(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); $(".js-drawer[data-drawer='compose']").on("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
$(document).on("uiDrawerActive", this.drawerToggleEvent); $(document).on("uiDrawerActive", this.drawerToggleEvent);
$(document).on("click", ".js-new-composer-opt-in", this.drawerToggleEvent);
} }
disabled(){ disabled(){
@@ -393,6 +394,7 @@ disabled(){
$(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent); $(".js-drawer[data-drawer='compose']").off("click", ".manage-templates-btn", this.manageTemplatesButtonClickEvent);
$(document).off("uiDrawerActive", this.drawerToggleEvent); $(document).off("uiDrawerActive", this.drawerToggleEvent);
$(document).off("click", ".js-new-composer-opt-in", this.drawerToggleEvent);
TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache; TD.mustaches["compose/docked_compose.mustache"] = this.prevComposeMustache;
} }

Some files were not shown because too many files have changed in this diff Show More