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

Compare commits

...

32 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
116 changed files with 2420 additions and 1489 deletions

View File

@@ -1,10 +1,10 @@
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.Data; using TweetDuck.Data;
using TweetLib.Core.Features.Configuration; using TweetLib.Core.Features.Configuration;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Twitter; using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
@@ -30,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;
@@ -57,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;
@@ -73,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

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,20 +1,19 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Windows.Forms; 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")] [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;
@@ -46,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){
@@ -87,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));
}); });
} }
@@ -102,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(){

View File

@@ -13,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){
@@ -56,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{ 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.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
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,9 +17,8 @@ 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.Resources;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Events; using TweetLib.Core.Features.Plugins.Events;
using TweetLib.Core.Features.Updates; using TweetLib.Core.Features.Updates;
@@ -65,7 +67,7 @@ namespace TweetDuck.Core{
Text = Program.BrandName; Text = Program.BrandName;
this.plugins = new PluginManager(this, Program.Config.Plugins, Program.PluginPath, Program.PluginDataPath); 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();
@@ -78,7 +80,6 @@ namespace TweetDuck.Core{
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);
@@ -236,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){
@@ -244,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);
});
} }
} }
@@ -286,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();
} }
@@ -319,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();
@@ -330,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);
} }
@@ -367,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();
@@ -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

@@ -6,19 +6,19 @@ 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.Features.Twitter;
using TweetLib.Core.Utils; 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();
protected static UserConfig Config => Program.Config.User;
private static ImageQuality ImageQuality => Config.TwitterImageQuality; private static ImageQuality ImageQuality => Config.TwitterImageQuality;
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500; private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
@@ -42,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)){
@@ -55,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");
@@ -80,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"));
@@ -108,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);
@@ -116,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: {
@@ -178,7 +178,7 @@ namespace TweetDuck.Core.Handling{
break; break;
case MenuOpenDevTools: case MenuOpenDevTools:
browserControl.ShowDevTools(); browserControl.OpenDevToolsCustom();
break; break;
} }
@@ -186,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){
@@ -194,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){
@@ -206,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){
@@ -217,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

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

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

@@ -11,7 +11,7 @@ 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){

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

@@ -3,7 +3,6 @@ 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.Plugins;
using TweetLib.Core.Data; using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
@@ -50,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);
} }
} }
} }
@@ -74,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){
@@ -141,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]);
} }
} }
@@ -159,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,20 +1,28 @@
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 => TweetDeckBridge.FontSize switch{ get => FontSize switch{
"largest" => 4, "largest" => 4,
"large" => 3, "large" => 3,
"small" => 1, "small" => 1,
@@ -28,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);
@@ -37,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();
} }
@@ -92,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;
@@ -120,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),
@@ -186,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,13 +2,15 @@
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.Plugins; using TweetDuck.Plugins;
using TweetDuck.Resources;
using TweetLib.Core.Data; using TweetLib.Core.Data;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
@@ -45,21 +47,21 @@ namespace TweetDuck.Core.Notification{
private int BaseClientWidth{ private int BaseClientWidth{
get => Config.NotificationSize switch{ get => Config.NotificationSize switch{
TweetNotification.Size.Custom => Config.CustomNotificationSize.Width, DesktopNotification.Size.Custom => Config.CustomNotificationSize.Width,
_ => BrowserUtils.Scale(284, SizeScale * (1.0 + 0.05 * FontSizeLevel)) _ => BrowserUtils.Scale(284, SizeScale * (1.0 + 0.05 * FontSizeLevel))
}; };
} }
private int BaseClientHeight{ private int BaseClientHeight{
get => Config.NotificationSize switch{ get => Config.NotificationSize switch{
TweetNotification.Size.Custom => Config.CustomNotificationSize.Height, DesktopNotification.Size.Custom => Config.CustomNotificationSize.Height,
_ => BrowserUtils.Scale(122, SizeScale * (1.0 + 0.08 * FontSizeLevel)) _ => BrowserUtils.Scale(122, SizeScale * (1.0 + 0.08 * FontSizeLevel))
}; };
} }
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();
@@ -73,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); plugins.Register(PluginEnvironment.Notification, new PluginDispatcher(browser));
mouseHookDelegate = MouseHookProc; mouseHookDelegate = MouseHookProc;
Disposed += (sender, args) => StopMouseHook(true); Disposed += (sender, args) => StopMouseHook(true);
@@ -100,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)){
@@ -154,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");
} }
} }
@@ -164,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();
@@ -180,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);
} }
@@ -217,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);
@@ -227,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;
@@ -237,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{
@@ -255,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.Plugins;
using TweetDuck.Resources;
using TweetLib.Core.Data; using TweetLib.Core.Data;
using TweetLib.Core.Features.Notifications;
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

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

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

@@ -8,8 +8,8 @@ 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;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Utils; using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
@@ -90,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();
} }
@@ -139,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;
} }
@@ -152,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,9 @@ 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 TweetLib.Core;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Utils; using TweetLib.Core.Utils;
@@ -127,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)"));
@@ -144,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{
@@ -168,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;
@@ -180,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{
@@ -218,17 +217,17 @@ namespace TweetDuck.Core.Other.Analytics{
private static string NotificationPosition{ private static string NotificationPosition{
get => UserConfig.NotificationPosition switch{ get => UserConfig.NotificationPosition switch{
TweetNotification.Position.TopLeft => "top left", DesktopNotification.Position.TopLeft => "top left",
TweetNotification.Position.TopRight => "top right", DesktopNotification.Position.TopRight => "top right",
TweetNotification.Position.BottomLeft => "bottom left", DesktopNotification.Position.BottomLeft => "bottom left",
TweetNotification.Position.BottomRight => "bottom right", DesktopNotification.Position.BottomRight => "bottom right",
_ => "custom" _ => "custom"
}; };
} }
private static string NotificationSize{ private static string NotificationSize{
get => UserConfig.NotificationSize switch{ get => UserConfig.NotificationSize switch{
TweetNotification.Size.Auto => "auto", DesktopNotification.Size.Auto => "auto",
_ => RoundUp(UserConfig.CustomNotificationSize.Width, 20) + "x" + RoundUp(UserConfig.CustomNotificationSize.Height, 20) _ => RoundUp(UserConfig.CustomNotificationSize.Width, 20) + "x" + RoundUp(UserConfig.CustomNotificationSize.Height, 20)
}; };
} }
@@ -286,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)";
} }
@@ -307,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,4 @@
using TweetDuck.Plugins; using TweetDuck.Core.Controls;
namespace TweetDuck.Core.Other { namespace TweetDuck.Core.Other {
partial class FormPlugins { partial class FormPlugins {
@@ -29,7 +29,7 @@ namespace TweetDuck.Core.Other {
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 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();
// //
@@ -119,7 +119,7 @@ namespace TweetDuck.Core.Other {
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 PluginListFlowLayout flowLayoutPlugins; private FlowLayoutPanelNoHScroll flowLayoutPlugins;
private System.Windows.Forms.Timer timerLayout; private System.Windows.Forms.Timer timerLayout;
} }
} }

View File

@@ -1,10 +1,10 @@
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 TweetLib.Core;
using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
@@ -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,7 +9,7 @@ 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 TweetLib.Core.Features.Updates; using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
@@ -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();

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

@@ -8,7 +8,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
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

@@ -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,7 +4,7 @@ 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.Plugins; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Utils; using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
@@ -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

@@ -24,7 +24,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
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

@@ -19,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;
@@ -51,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
@@ -80,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;
@@ -88,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="));
@@ -108,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;
@@ -126,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(){
@@ -137,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(){
@@ -189,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 + "%";
} }
} }
@@ -198,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
@@ -253,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){
@@ -355,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

@@ -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.Resources; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums; 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, 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

@@ -7,7 +7,7 @@ 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.Utils; using TweetLib.Core.Features.Twitter;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
@@ -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,16 +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);
} }
}; };
} }
public static void OpenExternalBrowser(string url){ public static void OpenDevToolsCustom(this IWebBrowser browser){
if (string.IsNullOrWhiteSpace(url))return; var info = new WindowInfo();
info.SetAsPopup(IntPtr.Zero, "Dev Tools");
switch(UrlUtils.Check(url)){ if (Config.DevToolsWindowOnTop){
case UrlUtils.CheckResult.Fine: info.ExStyle |= 0x00000008; // WS_EX_TOPMOST
}
browser.GetBrowserHost().ShowDevTools(info);
}
public static void OpenExternalBrowser(string url){
if (string.IsNullOrWhiteSpace(url)){
return;
}
switch(TwitterUrls.Check(url)){
case TwitterUrls.UrlType.Fine:
if (FormGuide.CheckGuideUrl(url, out string hash)){ if (FormGuide.CheckGuideUrl(url, out string hash)){
FormGuide.Show(hash); FormGuide.Show(hash);
} }
@@ -89,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);
} }
@@ -99,12 +119,12 @@ namespace TweetDuck.Core.Utils{
break; break;
case UrlUtils.CheckResult.Tracking: case TwitterUrls.UrlType.Tracking:
if (Config.IgnoreTrackingUrlWarning){ if (Config.IgnoreTrackingUrlWarning){
goto case UrlUtils.CheckResult.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);
@@ -117,20 +137,22 @@ namespace TweetDuck.Core.Utils{
} }
if (result == DialogResult.Ignore || result == DialogResult.Yes){ if (result == DialogResult.Ignore || result == DialogResult.Yes){
goto case UrlUtils.CheckResult.Fine; goto case TwitterUrls.UrlType.Fine;
} }
} }
break; break;
case UrlUtils.CheckResult.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;
@@ -152,20 +174,12 @@ namespace TweetDuck.Core.Utils{
} }
} }
else{ else{
OpenExternalBrowser(searchUrl+Uri.EscapeUriString(query)); OpenExternalBrowser(searchUrl + Uri.EscapeUriString(query));
} }
} }
public static string GetErrorName(CefErrorCode code){
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
}
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,7 +2,6 @@
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;
@@ -16,62 +15,37 @@ 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 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");
}
public static string GetMediaLink(string url, ImageQuality quality){
return ImageUrl.TryParse(url, out var obj) ? obj.WithQuality(quality) : url;
}
public static string GetImageFileName(string url){
return UrlUtils.GetFileNameFromUrl(ImageUrl.TryParse(url, out var obj) ? obj.WithNoQuality : 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 (ImageUrl.ValidExtensions.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 (FileUtils.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);
}); });
} }
} }
@@ -85,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){
@@ -111,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);
} }
} }
} }
@@ -119,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 = UrlUtils.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{
@@ -127,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);
}); });
} }
} }

View File

@@ -11,24 +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){
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 bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){ public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
@@ -44,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;
} }
} }
@@ -86,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);
@@ -114,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);
} }
} }

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

@@ -27,7 +27,7 @@ namespace TweetDuck.Plugins{
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;

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,180 +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.Controls;
using TweetDuck.Core.Utils;
using TweetDuck.Resources;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Plugins{
sealed class PluginManager : IPluginManager{
private const string SetupScriptPrefix = "plugins.";
public string PathCustomPlugins => Path.Combine(pluginFolder, PluginGroup.Custom.GetSubFolder());
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 pluginFolder;
private readonly string pluginDataFolder;
private readonly Control sync;
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(Control sync, IPluginConfig config, string pluginFolder, string pluginDataFolder){
this.Config = config;
this.Config.PluginChangedState += Config_PluginChangedState;
this.pluginFolder = pluginFolder;
this.pluginDataFolder = pluginDataFolder;
this.sync = sync;
this.bridge = new PluginBridge(this);
}
public void Register(IWebBrowser browser, PluginEnvironment environment, bool asMainBrowser = false){
browser.FrameLoadEnd += (sender, args) => {
IFrame frame = args.Frame;
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
ExecutePlugins(frame, environment);
}
};
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>(1);
foreach(var result in PluginGroupExtensions.Values.SelectMany(group => PluginLoader.AllInFolder(pluginFolder, pluginDataFolder, group))){
if (result.HasValue){
plugins.Add(result.Value);
}
else{
loadErrors.Add(result.Exception.Message);
}
}
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
}
private void ExecutePlugins(IFrame frame, PluginEnvironment environment){
if (!HasAnyPlugin(environment) || !ScriptLoader.ExecuteFile(frame, SetupScriptPrefix + environment.GetPluginScriptFile(), 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}");
}
sync.InvokeAsyncSafe(() => {
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
});
}
}
}

View File

@@ -12,7 +12,10 @@ 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.Impl;
using TweetDuck.Resources;
using TweetLib.Core; using TweetLib.Core;
using TweetLib.Core.Application.Helpers;
using TweetLib.Core.Collections; using TweetLib.Core.Collections;
using TweetLib.Core.Utils; using TweetLib.Core.Utils;
@@ -50,6 +53,7 @@ namespace TweetDuck{
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(){
Reporter = new Reporter(ErrorLogFilePath); Reporter = new Reporter(ErrorLogFilePath);
@@ -57,8 +61,17 @@ namespace TweetDuck{
Config = new ConfigManager(); Config = new ConfigManager();
#if DEBUG
Resources = new ScriptLoaderDebug();
#else
Resources = new ScriptLoader();
#endif
Lib.Initialize(new App.Builder{ Lib.Initialize(new App.Builder{
ErrorHandler = Reporter ErrorHandler = Reporter,
LockHandler = new LockHandler(),
SystemHandler = new SystemHandler(),
ResourceHandler = Resources
}); });
} }
@@ -71,7 +84,7 @@ namespace TweetDuck{
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore"); WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
if (!FileUtils.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;
} }
@@ -94,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){
@@ -128,7 +143,7 @@ namespace TweetDuck{
try{ try{
RequestHandlerBase.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze)); 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;
} }
@@ -139,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,
@@ -156,13 +171,14 @@ 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 || !FileUtils.CheckFolderWritePermission(ProgramPath); bool runElevated = !IsPortable || !FileUtils.CheckFolderWritePermission(ProgramPath);
if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){ if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
@@ -179,10 +195,10 @@ namespace TweetDuck{
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

@@ -19,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);
} }
}; };
@@ -58,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);
@@ -115,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;
} }
} }
} }

View File

@@ -1,45 +1,55 @@
using CefSharp; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetLib.Core.Application;
#if DEBUG
using System.Diagnostics;
using System.Reflection;
using TweetDuck.Core;
using TweetDuck.Plugins;
#endif
namespace TweetDuck.Resources{ namespace TweetDuck.Resources{
static class ScriptLoader{ class ScriptLoader : IAppResourceHandler{
private static readonly Dictionary<string, string> CachedData = new Dictionary<string, string>(16); private readonly Dictionary<string, string> cache = new Dictionary<string, string>(16);
private Control sync;
public static string LoadResourceSilent(string name){ public void Initialize(Control sync){
return LoadResource(name, null); this.sync = sync;
} }
public static string LoadResource(string name, Control sync){ protected void ClearCache(){
if (CachedData.TryGetValue(name, out string resourceData)){ cache.Clear();
}
public virtual void OnReloadTriggered(){
if (Control.ModifierKeys.HasFlag(Keys.Shift)){
ClearCache();
}
}
public string Load(string path) => LoadInternal(path, silent: false);
public string LoadSilent(string path) => LoadInternal(path, silent: true);
protected virtual string LocateFile(string path){
return Path.Combine(Program.ScriptPath, path);
}
private string LoadInternal(string path, bool silent){
if (sync == null){
throw new InvalidOperationException("Cannot use ScriptLoader before initialization.");
}
else if (sync.IsDisposed){
return null; // better than crashing I guess...?
}
if (cache.TryGetValue(path, out string resourceData)){
return resourceData; return resourceData;
} }
string path = Program.ScriptPath; string location = LocateFile(path);
#if DEBUG
if (Directory.Exists(HotSwapTargetDir)){
path = Path.Combine(HotSwapTargetDir, "scripts");
Debug.WriteLine("Hot swap active, redirecting "+name);
}
#endif
string resource; string resource;
try{ try{
string contents = File.ReadAllText(Path.Combine(path, name), Encoding.UTF8); string contents = File.ReadAllText(location, Encoding.UTF8);
int separator; int separator;
// first line can be either: // first line can be either:
@@ -47,120 +57,29 @@ namespace TweetDuck.Resources{
// #<version>\n // #<version>\n
if (contents[0] != '#'){ if (contents[0] != '#'){
ShowLoadError(sync, $"File {name} appears to be corrupted, please try reinstalling the app."); ShowLoadError(silent ? null : sync, $"File {path} appears to be corrupted, please try reinstalling the app.");
separator = 0; separator = 0;
} }
else{ else{
separator = contents.IndexOf('\n'); separator = contents.IndexOf('\n');
string fileVersion = contents.Substring(1, separator-1).TrimEnd(); string fileVersion = contents.Substring(1, separator - 1).TrimEnd();
if (fileVersion != Program.VersionTag){ if (fileVersion != Program.VersionTag){
ShowLoadError(sync, $"File {name} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app."); ShowLoadError(silent ? null : sync, $"File {path} is made for a different version of TweetDuck ({fileVersion}) and may not function correctly in this version, please try reinstalling the app.");
} }
} }
resource = contents.Substring(separator).TrimStart(); resource = contents.Substring(separator).TrimStart();
}catch(Exception ex){ }catch(Exception ex){
ShowLoadError(sync, $"Could not load {name}. The program will continue running with limited functionality.\n\n{ex.Message}"); ShowLoadError(silent ? null : sync, $"Could not load {path}. The program will continue running with limited functionality.\n\n{ex.Message}");
resource = null; resource = null;
} }
return CachedData[name] = resource; return cache[path] = resource;
}
public static bool ExecuteFile(IFrame frame, string file, Control sync){
string script = LoadResource(file, sync);
ExecuteScript(frame, script, "root:"+Path.GetFileNameWithoutExtension(file));
return script != null;
}
public static void ExecuteScript(IFrame frame, string script, string identifier){
if (script != null){
frame.ExecuteJavaScriptAsync(script, identifier, 1);
}
}
public static void ClearCache(){
CachedData.Clear();
} }
private static void ShowLoadError(Control sync, string message){ private static void ShowLoadError(Control sync, string message){
sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK)); sync?.InvokeSafe(() => FormMessage.Error("Resource Error", message, FormMessage.OK));
} }
#if DEBUG
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "bld", "post_build.exe");
static ScriptLoader(){
if (File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Activating resource hot swap...");
ResetHotSwap();
Application.ApplicationExit += (sender, args) => ResetHotSwap();
}
}
public static void HotSwap(){
if (!File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Failed resource hot swap, missing rebuild script: "+HotSwapRebuildScript);
return;
}
ResetHotSwap();
Directory.CreateDirectory(HotSwapTargetDir);
Stopwatch sw = Stopwatch.StartNew();
using(Process process = Process.Start(new ProcessStartInfo{
FileName = HotSwapRebuildScript,
Arguments = $"\"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
WindowStyle = ProcessWindowStyle.Hidden
})){
// ReSharper disable once PossibleNullReferenceException
if (!process.WaitForExit(8000)){
Debug.WriteLine("Failed resource hot swap, script did not finish in time");
return;
}
else if (process.ExitCode != 0){
Debug.WriteLine("Failed resource hot swap, script exited with code "+process.ExitCode);
return;
}
}
sw.Stop();
Debug.WriteLine("Finished rebuild script in "+sw.ElapsedMilliseconds+" ms");
ClearCache();
// Force update plugin manager setup scripts
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
Type typePluginManager = typeof(PluginManager);
Type typeFormBrowser = typeof(FormBrowser);
// ReSharper disable PossibleNullReferenceException
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
typePluginManager.GetField("pluginFolder", flagsInstance).SetValue(instPluginManager, newPluginRoot);
Debug.WriteLine("Reloading hot swapped plugins...");
((PluginManager)instPluginManager).Reload();
// ReSharper restore PossibleNullReferenceException
}
private static void ResetHotSwap(){
try{
Directory.Delete(HotSwapTargetDir, true);
}catch(DirectoryNotFoundException){}
}
private static string FixPathSlash(string path){
return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)+'\\';
}
#endif
} }
} }

View File

@@ -0,0 +1,100 @@
#if DEBUG
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using TweetDuck.Core;
using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Resources{
sealed class ScriptLoaderDebug : ScriptLoader{
private static readonly string HotSwapProjectRoot = FixPathSlash(Path.GetFullPath(Path.Combine(Program.ProgramPath, "../../../")));
private static readonly string HotSwapTargetDir = FixPathSlash(Path.Combine(HotSwapProjectRoot, "bin", "tmp"));
private static readonly string HotSwapRebuildScript = Path.Combine(HotSwapProjectRoot, "bld", "post_build.exe");
private static string FixPathSlash(string path){
return path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + '\\';
}
public ScriptLoaderDebug(){
if (File.Exists(HotSwapRebuildScript)){
Debug.WriteLine("Activating resource hot swap...");
ResetHotSwap();
Application.ApplicationExit += (sender, args) => ResetHotSwap();
}
}
public override void OnReloadTriggered(){
HotSwap();
}
protected override string LocateFile(string path){
if (Directory.Exists(HotSwapTargetDir)){
Debug.WriteLine($"Hot swap active, redirecting {path}");
return Path.Combine(HotSwapTargetDir, "scripts", path);
}
return base.LocateFile(path);
}
private void HotSwap(){
if (!File.Exists(HotSwapRebuildScript)){
Debug.WriteLine($"Failed resource hot swap, missing rebuild script: {HotSwapRebuildScript}");
return;
}
ResetHotSwap();
Directory.CreateDirectory(HotSwapTargetDir);
Stopwatch sw = Stopwatch.StartNew();
using(Process process = Process.Start(new ProcessStartInfo{
FileName = HotSwapRebuildScript,
Arguments = $"\"{HotSwapTargetDir}\\\" \"{HotSwapProjectRoot}\\\" \"Debug\" \"{Program.VersionTag}\"",
WindowStyle = ProcessWindowStyle.Hidden
})){
// ReSharper disable once PossibleNullReferenceException
if (!process.WaitForExit(8000)){
Debug.WriteLine("Failed resource hot swap, script did not finish in time");
return;
}
else if (process.ExitCode != 0){
Debug.WriteLine($"Failed resource hot swap, script exited with code {process.ExitCode}");
return;
}
}
sw.Stop();
Debug.WriteLine($"Finished rebuild script in {sw.ElapsedMilliseconds} ms");
ClearCache();
// Force update plugin manager setup scripts
string newPluginRoot = Path.Combine(HotSwapTargetDir, "plugins");
const BindingFlags flagsInstance = BindingFlags.Instance | BindingFlags.NonPublic;
Type typePluginManager = typeof(PluginManager);
Type typeFormBrowser = typeof(FormBrowser);
// ReSharper disable PossibleNullReferenceException
object instPluginManager = typeFormBrowser.GetField("plugins", flagsInstance).GetValue(FormManager.TryFind<FormBrowser>());
typePluginManager.GetField("pluginFolder", flagsInstance).SetValue(instPluginManager, newPluginRoot);
Debug.WriteLine("Reloading hot swapped plugins...");
((PluginManager)instPluginManager).Reload();
// ReSharper restore PossibleNullReferenceException
}
private void ResetHotSwap(){
try{
Directory.Delete(HotSwapTargetDir, true);
}catch(DirectoryNotFoundException){}
}
}
}
#endif

View File

@@ -945,31 +945,42 @@
// Block: Allow drag & drop behavior for dropping links on columns to open their detail view. // Block: Allow drag & drop behavior for dropping links on columns to open their detail view.
// //
execSafe(function supportDragDropOverColumns(){ execSafe(function supportDragDropOverColumns(){
const tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/; const regexTweet = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/;
const selector = "section.js-column"; const regexAccount = /^https?:\/\/twitter\.com\/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)\/?$/;
let isDraggingValid = false; let dragType = false;
const events = { const events = {
dragover: function(e){ dragover: function(e){
e.originalEvent.dataTransfer.dropEffect = isDraggingValid ? "all" : "none"; e.originalEvent.dataTransfer.dropEffect = dragType ? "all" : "none";
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}, },
drop: function(e){ drop: function(e){
let match = tweetRegex.exec(e.originalEvent.dataTransfer.getData("URL")); let url = e.originalEvent.dataTransfer.getData("URL");
if (match.length === 2){ if (dragType === "tweet"){
let column = TD.controller.columnManager.get($(this).attr("data-column")); let match = regexTweet.exec(url);
if (column){ if (match.length === 2){
TD.controller.clients.getPreferredClient().show(match[1], function(chirp){ let column = TD.controller.columnManager.get($(this).attr("data-column"));
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
$(document).trigger("uiGridClearSelection"); if (column){
}, function(){ TD.controller.clients.getPreferredClient().show(match[1], function(chirp){
alert("error|Could not retrieve the requested tweet."); TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
}); $(document).trigger("uiGridClearSelection");
}, function(){
alert("error|Could not retrieve the requested tweet.");
});
}
}
}
else if (dragType === "account"){
let match = regexAccount.exec(url);
if (match.length === 2){
$(document).trigger("uiShowProfile", { id: match[1] });
} }
} }
@@ -978,13 +989,20 @@
} }
}; };
const selectors = {
tweet: "section.js-column",
account: app
};
window.TDGF_onGlobalDragStart = function(type, data){ window.TDGF_onGlobalDragStart = function(type, data){
if (type === "link"){ if (dragType){
isDraggingValid = tweetRegex.test(data); app.undelegate(selectors[dragType], events);
app.delegate(selector, events); dragType = null;
} }
else{
app.undelegate(selector, events); if (type === "link"){
dragType = regexTweet.test(data) ? "tweet" : regexAccount.test(data) ? "account": null;
app.delegate(selectors[dragType], events);
} }
}; };
}); });
@@ -1065,6 +1083,19 @@
}); });
}); });
//
// Block: Allow changing first day of week in date picker.
//
if (ensurePropertyExists($, "tools", "dateinput", "conf", "firstDay")){
$.tools.dateinput.conf.firstDay = $TDX.firstDayOfWeek;
onAppReady.push(function setupDatePickerFirstDayCallback(){
window.TDGF_registerPropertyUpdateCallback(function($TDX){
$.tools.dateinput.conf.firstDay = $TDX.firstDayOfWeek;
});
});
}
// //
// Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal. // Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
// //
@@ -1212,14 +1243,14 @@
// Block: Setup video player hooks. // Block: Setup video player hooks.
// //
execSafe(function setupVideoPlayer(){ execSafe(function setupVideoPlayer(){
window.TDGF_playVideo = function(url, username){ window.TDGF_playVideo = function(videoUrl, tweetUrl, username){
return if !url; return if !videoUrl;
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){ $TD.playVideo(videoUrl, tweetUrl || videoUrl, username || null, function(){
$TD.playVideo(null, null); $('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
}).appendTo(app); $TD.stopVideo();
}).appendTo(app);
$TD.playVideo(url, username || null); });
}; };
const getGifLink = function(ele){ const getGifLink = function(ele){
@@ -1239,13 +1270,14 @@
app.delegate(".js-gif-play", { app.delegate(".js-gif-play", {
click: function(e){ click: function(e){
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video")); let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
let tweet = getVideoTweetLink($(this));
if (src){ if (src){
let hovered = getHoveredTweet(); let hovered = getHoveredTweet();
window.TDGF_playVideo(src, getUsername(hovered && hovered.obj)); window.TDGF_playVideo(src, tweet, getUsername(hovered && hovered.obj));
} }
else{ else{
$TD.openBrowser(getVideoTweetLink($(this))); $TD.openBrowser(tweet);
} }
e.stopPropagation(); e.stopPropagation();
@@ -1279,7 +1311,7 @@
let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId); let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
if (media && media.isVideo && media.service === "twitter"){ if (media && media.isVideo && media.service === "twitter"){
window.TDGF_playVideo(media.chooseVideoVariant().url, getUsername(this.chirp)); window.TDGF_playVideo(media.chooseVideoVariant().url, this.chirp.getChirpURL(), getUsername(this.chirp));
cancelModal = true; cancelModal = true;
} }
}); });
@@ -1457,6 +1489,24 @@
}; };
} }
//
// Block: Add missing languages for Bing Translator (Bengali, Icelandic, Tagalog, Tamil, Telugu, Urdu).
//
if (ensurePropertyExists(TD, "languages", "getSupportedTranslationSourceLanguages")){
const newCodes = [ "bn", "is", "tl", "ta", "te", "ur" ];
const codeSet = new Set(TD.languages.getSupportedTranslationSourceLanguages());
for(const lang of newCodes){
codeSet.add(lang);
}
const codeList = [...codeSet];
TD.languages.getSupportedTranslationSourceLanguages = function(){
return codeList;
};
}
// //
// Block: Setup global function to refresh all columns. // Block: Setup global function to refresh all columns.
// //
@@ -1679,6 +1729,21 @@
window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times window.TDGF_reload = function(){}; // redefine to prevent reloading multiple times
}; };
//
// Block: Setup global functions to respond to updating $TDX properties.
//
(function(){
var callbacks = [];
window.TDGF_registerPropertyUpdateCallback = function(callback){
callbacks.push(callback);
};
window.TDGF_onPropertiesUpdated = function(){
callbacks.forEach(func => func($TDX));
};
})();
// //
// Block: Disable default TweetDeck update notification. // Block: Disable default TweetDeck update notification.
// //

View File

@@ -120,10 +120,19 @@
// Block: Setup bridges to global functions. // Block: Setup bridges to global functions.
// //
window.TDPF_getColumnName = window.TDGF_getColumnName; window.TDPF_getColumnName = window.TDGF_getColumnName;
window.TDPF_playVideo = window.TDGF_playVideo;
window.TDPF_reloadColumns = window.TDGF_reloadColumns; window.TDPF_reloadColumns = window.TDGF_reloadColumns;
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent; window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
window.TDPF_injectMustache = window.TDGF_injectMustache; window.TDPF_injectMustache = window.TDGF_injectMustache;
window.TDPF_registerPropertyUpdateCallback = window.TDGF_registerPropertyUpdateCallback;
window.TDPF_playVideo = function(urlOrObject, username){
if (typeof urlOrObject === "string"){
window.TDGF_playVideo(urlOrObject, null, username);
}
else{
window.TDGF_playVideo(urlOrObject.videoUrl, urlOrObject.tweetUrl, urlOrObject.username);
}
};
#import "scripts/plugins.base.js" #import "scripts/plugins.base.js"
})(); })();

View File

@@ -40,78 +40,50 @@
/* General styling */ /* General styling */
/*******************/ /*******************/
* {
border-radius: 0 !important;
}
body { body {
/* remove scrollbar */
overflow: hidden !important; overflow: hidden !important;
} }
.page-canvas { .page-canvas, div[tweetduck-login-wrapper], body.ResponsiveLayout > div.PageContainer > div.Section {
/* tweak page shadow */
box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important; box-shadow: 0 0 150px rgba(255, 255, 255, 0.3) !important;
} }
.topbar, .TopNav { .topbar, .TopNav {
/* hide top bar */
display: none !important; display: none !important;
} }
.page-canvas, .buttons, .btn, input {
/* sharpen borders */
border-radius: 0 !important;
}
input { input {
/* tweak input padding */
padding: 5px 8px 4px !important; padding: 5px 8px 4px !important;
} }
button[type='submit'] { button[type='submit'] {
/* style buttons */
border: 1px solid rgba(0, 0, 0, 0.3) !important; border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
} }
.tweetduck-helper { /****************************/
/* custom login text */ /* General per-page styling */
/****************************/
html[mobile][login] div[tweetduck-login-wrapper] {
/* vertically center page & fix colors */
margin-top: calc(50vh - 200px);
padding: 26px 1.1vw;
background-color: white;
}
html[mobile][login] #tweetduck-helper:hover {
text-decoration: underline;
}
html[desktop][login] #tweetduck-helper {
margin-top: 15px !important; margin-top: 15px !important;
font-weight: bold !important; font-weight: bold !important;
} }
/********************************************/ html[mobile][logout] div[role="button"] {
/* Fix min width and margins on logout page */
/********************************************/
html[logout] .page-canvas {
width: auto !important;
max-width: 888px;
}
html[logout] .signout-wrapper {
width: auto !important;
margin: 0 auto !important;
}
html[logout] .signout {
margin: 60px 0 54px !important;
}
html[logout] .buttons {
padding-bottom: 0 !important;
}
/*******************************/
/* General logout page styling */
/*******************************/
html[logout] .aside {
/* hide elements around dialog */
display: none;
}
html[logout] .buttons button, html[logout] .buttons a {
/* style buttons */
display: inline-block;
margin: 0 4px !important;
border: 1px solid rgba(0, 0, 0, 0.3) !important; border: 1px solid rgba(0, 0, 0, 0.3) !important;
border-radius: 0 !important;
} }

View File

@@ -1,4 +1,8 @@
(function(){ (function(){
const isLogin = location.pathname === "/login";
const isLogout = location.pathname === "/logout";
const isMobile = location.host === "mobile.twitter.com";
// //
// Function: Inject custom CSS into the page. // Function: Inject custom CSS into the page.
// //
@@ -14,51 +18,119 @@
document.head.appendChild(link); document.head.appendChild(link);
if (location.pathname === "/logout"){ if (isLogin){
document.documentElement.setAttribute("login", "");
}
else if (isLogout){
document.documentElement.setAttribute("logout", ""); document.documentElement.setAttribute("logout", "");
} }
if (isMobile){
document.documentElement.setAttribute("mobile", "");
}
else{
document.documentElement.setAttribute("desktop", "");
}
}; };
setTimeout(injectCSS, 1); setTimeout(injectCSS, 1);
// //
// Block: Make login page links external. // Function: Trigger once element exists.
// //
if (location.pathname === "/login"){ const triggerWhenExists = function(query, callback){
document.addEventListener("DOMContentLoaded", function(){ let id = window.setInterval(function(){
const openLinkExternally = function(e){ let ele = document.querySelector(query);
let href = e.currentTarget.getAttribute("href");
$TD.openBrowser(href[0] === '/' ? location.origin+href : href);
e.preventDefault();
e.stopPropagation();
};
for(let link of document.getElementsByTagName("A")){ if (ele && callback(ele)){
link.addEventListener("click", openLinkExternally); window.clearInterval(id);
} }
}, 5);
let texts = document.querySelector(".page-canvas > div:last-child"); };
if (texts){ //
texts.insertAdjacentHTML("beforeend", `<p class="tweetduck-helper">Used the TweetDuck app before? <a href="#">Import your profile »</a></p>`); // Block: Add profile import button & enable custom styling, make page links external on old login page.
//
texts.querySelector(".tweetduck-helper > a").addEventListener("click", function(){ if (isLogin){
$TD.openProfileImport(); document.addEventListener("DOMContentLoaded", function(){
if (isMobile){
triggerWhenExists("main h1", function(heading){
heading.parentNode.setAttribute("tweetduck-login-wrapper", "");
return true;
}); });
triggerWhenExists("a[href='/i/flow/signup']", function(texts){
texts = texts.parentNode;
let link = texts.childNodes[0];
let separator = texts.childNodes[1];
if (link && separator){
texts.classList.add("tweetduck-login-links");
link = link.cloneNode(false);
link.id = "tweetduck-helper";
link.href = "#";
link.innerText = "Import TweetDuck profile";
texts.appendChild(separator.cloneNode(true));
texts.appendChild(link);
link.addEventListener("click", function(){
$TD.openProfileImport();
});
return true;
}
else{
return false;
}
});
}
else{
const openLinkExternally = function(e){
let href = e.currentTarget.getAttribute("href");
$TD.openBrowser(href[0] === '/' ? location.origin+href : href);
e.preventDefault();
e.stopPropagation();
};
for(let link of document.getElementsByTagName("A")){
link.addEventListener("click", openLinkExternally);
}
let texts = document.querySelector(".page-canvas > div:last-child");
if (texts){
texts.insertAdjacentHTML("beforeend", `<p id="tweetduck-helper">Used the TweetDuck app before? <a href="#">Import your profile »</a></p>`);
texts.querySelector("#tweetduck-helper > a").addEventListener("click", function(){
$TD.openProfileImport();
});
}
} }
}); });
} }
// //
// Block: Fix broken Cancel button on logout page. // Block: Hide cookie crap.
// //
else if (location.pathname === "/logout"){ if (isMobile){
document.addEventListener("DOMContentLoaded", function(){ document.addEventListener("DOMContentLoaded", function(){
let cancel = document.querySelector(".buttons .cancel"); triggerWhenExists("a[href^='https://help.twitter.com/rules-and-policies/twitter-cookies']", function(cookie){
while(!!cookie){
if (cancel && cancel.tagName === "A"){ if (cookie.offsetHeight > 30){
cancel.href = "https://tweetdeck.twitter.com/"; cookie.remove();
} return true;
}
else{
cookie = cookie.parentNode;
}
}
return false;
});
}); });
} }
})(); })();

View File

@@ -92,7 +92,6 @@
}); });
onClick(ele.querySelector(".tdu-btn-later"), function(){ onClick(ele.querySelector(".tdu-btn-later"), function(){
$TDU.onUpdateDelayed();
exitSlide(); exitSlide();
}); });

View File

@@ -55,9 +55,9 @@
<ItemGroup> <ItemGroup>
<Compile Include="Configuration\Arguments.cs" /> <Compile Include="Configuration\Arguments.cs" />
<Compile Include="Configuration\ConfigManager.cs" /> <Compile Include="Configuration\ConfigManager.cs" />
<Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\SystemConfig.cs" /> <Compile Include="Configuration\SystemConfig.cs" />
<Compile Include="Configuration\UserConfig.cs" /> <Compile Include="Configuration\UserConfig.cs" />
<Compile Include="Core\Adapters\CefScriptExecutor.cs" />
<Compile Include="Core\Bridge\PropertyBridge.cs" /> <Compile Include="Core\Bridge\PropertyBridge.cs" />
<Compile Include="Core\Bridge\UpdateBridge.cs" /> <Compile Include="Core\Bridge\UpdateBridge.cs" />
<Compile Include="Core\Controls\ControlExtensions.cs" /> <Compile Include="Core\Controls\ControlExtensions.cs" />
@@ -121,7 +121,6 @@
<DependentUpon>FormNotificationTweet.cs</DependentUpon> <DependentUpon>FormNotificationTweet.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Notification\SoundNotification.cs" /> <Compile Include="Core\Notification\SoundNotification.cs" />
<Compile Include="Core\Notification\TweetNotification.cs" />
<Compile Include="Core\Other\Analytics\AnalyticsFile.cs" /> <Compile Include="Core\Other\Analytics\AnalyticsFile.cs" />
<Compile Include="Core\Other\Analytics\AnalyticsManager.cs" /> <Compile Include="Core\Other\Analytics\AnalyticsManager.cs" />
<Compile Include="Core\Other\Analytics\AnalyticsReport.cs" /> <Compile Include="Core\Other\Analytics\AnalyticsReport.cs" />
@@ -157,6 +156,12 @@
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.Designer.cs"> <Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.Designer.cs">
<DependentUpon>DialogSettingsAnalytics.cs</DependentUpon> <DependentUpon>DialogSettingsAnalytics.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsExternalProgram.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsExternalProgram.Designer.cs">
<DependentUpon>DialogSettingsExternalProgram.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsSearchEngine.cs"> <Compile Include="Core\Other\Settings\Dialogs\DialogSettingsSearchEngine.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -193,6 +198,12 @@
<Compile Include="Core\Other\Settings\TabSettingsFeedback.Designer.cs"> <Compile Include="Core\Other\Settings\TabSettingsFeedback.Designer.cs">
<DependentUpon>TabSettingsFeedback.cs</DependentUpon> <DependentUpon>TabSettingsFeedback.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\Settings\TabSettingsTray.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsTray.Designer.cs">
<DependentUpon>TabSettingsTray.cs</DependentUpon>
</Compile>
<Compile Include="Core\TweetDeckBrowser.cs" /> <Compile Include="Core\TweetDeckBrowser.cs" />
<Compile Include="Core\Utils\TwitterUtils.cs" /> <Compile Include="Core\Utils\TwitterUtils.cs" />
<Compile Include="Core\Management\ProfileManager.cs" /> <Compile Include="Core\Management\ProfileManager.cs" />
@@ -238,23 +249,26 @@
<Compile Include="Core\Other\FormSettings.Designer.cs"> <Compile Include="Core\Other\FormSettings.Designer.cs">
<DependentUpon>FormSettings.cs</DependentUpon> <DependentUpon>FormSettings.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Impl\LockHandler.cs" />
<Compile Include="Impl\SystemHandler.cs" />
<Compile Include="Plugins\PluginControl.cs"> <Compile Include="Plugins\PluginControl.cs">
<SubType>UserControl</SubType> <SubType>UserControl</SubType>
</Compile> </Compile>
<Compile Include="Plugins\PluginControl.Designer.cs"> <Compile Include="Plugins\PluginControl.Designer.cs">
<DependentUpon>PluginControl.cs</DependentUpon> <DependentUpon>PluginControl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Plugins\PluginListFlowLayout.cs"> <Compile Include="Plugins\PluginDispatcher.cs" />
<Compile Include="Core\Controls\FlowLayoutPanelNoHScroll.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Configuration\PluginConfig.cs" /> <Compile Include="Configuration\PluginConfig.cs" />
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Properties\Resources.Designer.cs"> <Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen> <AutoGen>True</AutoGen>
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
</Compile> </Compile>
<Compile Include="Reporter.cs" /> <Compile Include="Reporter.cs" />
<Compile Include="Resources\ScriptLoaderDebug.cs" />
<Compile Include="Updates\FormUpdateDownload.cs"> <Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -394,7 +408,7 @@ IF EXIST "$(ProjectDir)bld\post_build.exe" (
<Exec Command="start &quot;&quot; /B &quot;ISCC.exe&quot; /Q &quot;$(ProjectDir)bld\gen_upd.iss&quot;" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" /> <Exec Command="start &quot;&quot; /B &quot;ISCC.exe&quot; /Q &quot;$(ProjectDir)bld\gen_upd.iss&quot;" WorkingDirectory="$(ProjectDir)bld\" IgnoreExitCode="true" />
</Target> </Target>
<PropertyGroup> <PropertyGroup>
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent> <PreBuildEvent>powershell -NoProfile -Command "$ErrorActionPreference = 'SilentlyContinue'; (Get-Process TweetDuck.Browser | Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'}).Kill(); Exit 0"</PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>

View File

@@ -11,8 +11,8 @@ namespace TweetDuck.Updates{
this.updateInfo = info; this.updateInfo = info;
Text = "Updating "+Program.BrandName; Text = "Updating " + Program.BrandName;
labelDescription.Text = "Downloading version "+info.VersionTag+"..."; labelDescription.Text = $"Downloading version {info.VersionTag}...";
timerDownloadCheck.Start(); timerDownloadCheck.Start();
} }

View File

@@ -49,11 +49,11 @@ namespace TweetDuck.Updates{
} }
private UpdateInfo ParseFromJson(string json){ private UpdateInfo ParseFromJson(string json){
bool IsUpdaterAsset(JsonObject obj){ static bool IsUpdaterAsset(JsonObject obj){
return UpdaterAssetName == (string)obj["name"]; return UpdaterAssetName == (string)obj["name"];
} }
string AssetDownloadUrl(JsonObject obj){ static string AssetDownloadUrl(JsonObject obj){
return (string)obj["browser_download_url"]; return (string)obj["browser_download_url"];
} }

View File

@@ -13,7 +13,7 @@ namespace TweetLib.Communication{
public static Client CreateClient(string token){ public static Client CreateClient(string token){
int space = token.IndexOf(' '); int space = token.IndexOf(' ');
return new Client(token.Substring(0, space), token.Substring(space+1)); return new Client(token.Substring(0, space), token.Substring(space + 1));
} }
protected readonly PipeStream pipeIn; protected readonly PipeStream pipeIn;
@@ -74,7 +74,7 @@ namespace TweetLib.Communication{
internal Server() : base(new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable), new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)){} internal Server() : base(new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable), new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)){}
public string GenerateToken(){ public string GenerateToken(){
return ServerPipeIn.GetClientHandleAsString()+" "+ServerPipeOut.GetClientHandleAsString(); return ServerPipeIn.GetClientHandleAsString() + " " + ServerPipeOut.GetClientHandleAsString();
} }
public void DisposeToken(){ public void DisposeToken(){
@@ -100,7 +100,7 @@ namespace TweetLib.Communication{
} }
else{ else{
Key = line.Substring(0, separatorIndex); Key = line.Substring(0, separatorIndex);
Data = line.Substring(separatorIndex+1); Data = line.Substring(separatorIndex + 1);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" /> <Import Project="..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\packages\Microsoft.Net.Compilers.3.0.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
@@ -11,6 +11,7 @@
<RootNamespace>TweetLib.Communication</RootNamespace> <RootNamespace>TweetLib.Communication</RootNamespace>
<AssemblyName>TweetLib.Communication</AssemblyName> <AssemblyName>TweetLib.Communication</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<LangVersion>8.0</LangVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
@@ -23,7 +24,6 @@
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>8.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
@@ -33,7 +33,6 @@
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View File

@@ -4,16 +4,25 @@ using TweetLib.Core.Application;
namespace TweetLib.Core{ namespace TweetLib.Core{
public sealed class App{ public sealed class App{
public static IAppErrorHandler ErrorHandler { get; private set; } public static IAppErrorHandler ErrorHandler { get; private set; }
public static IAppLockHandler LockHandler { get; private set; }
public static IAppSystemHandler SystemHandler { get; private set; }
public static IAppResourceHandler ResourceHandler { get; private set; }
// Builder // Builder
public sealed class Builder{ public sealed class Builder{
public IAppErrorHandler? ErrorHandler { get; set; } public IAppErrorHandler? ErrorHandler { get; set; }
public IAppLockHandler? LockHandler { get; set; }
public IAppSystemHandler? SystemHandler { get; set; }
public IAppResourceHandler? ResourceHandler { get; set; }
// Validation // Validation
internal void Initialize(){ internal void Initialize(){
App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!; App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
App.LockHandler = Validate(LockHandler, nameof(LockHandler))!;
App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
} }
private T Validate<T>(T obj, string name){ private T Validate<T>(T obj, string name){

View File

@@ -1,12 +1,11 @@
using System; using System;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using TweetDuck.Core.Utils;
namespace TweetDuck.Configuration{ namespace TweetLib.Core.Application.Helpers{
sealed class LockManager{ public sealed class LockManager{
private const int RetryDelay = 250; private const int RetryDelay = 250;
public enum Result{ public enum Result{
@@ -14,8 +13,8 @@ namespace TweetDuck.Configuration{
} }
private readonly string file; private readonly string file;
private FileStream lockStream; private FileStream? lockStream;
private Process lockingProcess; private Process? lockingProcess;
public LockManager(string file){ public LockManager(string file){
this.file = file; this.file = file;
@@ -37,7 +36,7 @@ namespace TweetDuck.Configuration{
private Result TryCreateLockFile(){ private Result TryCreateLockFile(){
void CreateLockFileStream(){ void CreateLockFileStream(){
lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read); lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
lockStream.Write(BitConverter.GetBytes(WindowsUtils.CurrentProcessID), 0, sizeof(int)); lockStream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
lockStream.Flush(true); lockStream.Flush(true);
} }
@@ -82,13 +81,11 @@ namespace TweetDuck.Configuration{
try{ try{
Process foundProcess = Process.GetProcessById(pid); Process foundProcess = Process.GetProcessById(pid);
using(Process currentProcess = Process.GetCurrentProcess()){ if (MatchesCurrentProcess(foundProcess)){
if (foundProcess.MainModule.FileVersionInfo.InternalName == currentProcess.MainModule.FileVersionInfo.InternalName){ lockingProcess = foundProcess;
lockingProcess = foundProcess; }
} else{
else{ foundProcess.Close();
foundProcess.Close();
}
} }
}catch{ }catch{
// GetProcessById throws ArgumentException if the process is missing // GetProcessById throws ArgumentException if the process is missing
@@ -124,7 +121,7 @@ namespace TweetDuck.Configuration{
try{ try{
File.Delete(file); File.Delete(file);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.LogImportant(e.ToString()); App.ErrorHandler.Log(e.ToString());
return false; return false;
} }
} }
@@ -133,51 +130,33 @@ namespace TweetDuck.Configuration{
} }
// Locking process // Locking process
public bool RestoreLockingProcess(){
return lockingProcess != null && App.LockHandler.RestoreProcess(lockingProcess);
}
public bool RestoreLockingProcess(int failTimeout){ public bool CloseLockingProcess(){
if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray if (lockingProcess != null && App.LockHandler.CloseProcess(lockingProcess)){
NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0); lockingProcess = null;
return true;
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
return true;
}
} }
return false; return false;
} }
public bool CloseLockingProcess(int closeTimeout, int killTimeout){ // Utilities
if (lockingProcess != null){
try{
if (lockingProcess.CloseMainWindow()){
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, closeTimeout, RetryDelay);
}
if (!lockingProcess.HasExited){ private static int CurrentProcessID{
lockingProcess.Kill(); get{
WindowsUtils.TrySleepUntil(CheckLockingProcessExited, killTimeout, RetryDelay); using Process me = Process.GetCurrentProcess();
} return me.Id;
if (lockingProcess.HasExited){
lockingProcess.Dispose();
lockingProcess = null;
return true;
}
}catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
if (lockingProcess != null){
bool hasExited = CheckLockingProcessExited();
lockingProcess.Dispose();
return hasExited;
}
}
} }
return false;
} }
private bool CheckLockingProcessExited(){ [SuppressMessage("ReSharper", "PossibleNullReferenceException")]
lockingProcess.Refresh(); private static bool MatchesCurrentProcess(Process process){
return lockingProcess.HasExited; using Process current = Process.GetCurrentProcess();
return current.MainModule.FileVersionInfo.InternalName == process.MainModule.FileVersionInfo.InternalName;
} }
} }
} }

View File

@@ -0,0 +1,8 @@
using System.Diagnostics;
namespace TweetLib.Core.Application{
public interface IAppLockHandler{
bool RestoreProcess(Process process);
bool CloseProcess(Process process);
}
}

View File

@@ -0,0 +1,5 @@
namespace TweetLib.Core.Application{
public interface IAppResourceHandler{
string? Load(string path);
}
}

View File

@@ -0,0 +1,5 @@
namespace TweetLib.Core.Application{
public interface IAppSystemHandler{
void OpenFileExplorer(string path);
}
}

View File

@@ -0,0 +1,7 @@
namespace TweetLib.Core.Browser{
public interface IScriptExecutor{
void RunFunction(string name, params object[] args);
void RunScript(string identifier, string script);
bool RunFile(string file);
}
}

View File

@@ -16,7 +16,7 @@ namespace TweetLib.Core.Collections{
if (entry.Length > 0 && entry[0] == entryChar){ if (entry.Length > 0 && entry[0] == entryChar){
if (index < array.Length - 1){ if (index < array.Length - 1){
string potentialValue = array[index+1]; string potentialValue = array[index + 1];
if (potentialValue.Length > 0 && potentialValue[0] == entryChar){ if (potentialValue.Length > 0 && potentialValue[0] == entryChar){
targetArgs.AddFlag(entry); targetArgs.AddFlag(entry);

View File

@@ -21,7 +21,7 @@ namespace TweetLib.Core.Data{
byte[] name = Encoding.UTF8.GetBytes(identifier); byte[] name = Encoding.UTF8.GetBytes(identifier);
if (name.Length > 255){ if (name.Length > 255){
throw new ArgumentOutOfRangeException("Identifier cannot be 256 or more characters long: "+identifier); throw new ArgumentOutOfRangeException("Identifier cannot be 256 or more characters long: " + identifier);
} }
byte[] contents; byte[] contents;
@@ -103,7 +103,7 @@ namespace TweetLib.Core.Data{
public string[] KeyValue{ public string[] KeyValue{
get{ get{
int index = Identifier.IndexOf(KeySeparator); int index = Identifier.IndexOf(KeySeparator);
return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index+1).Split(KeySeparator); return index == -1 ? StringUtils.EmptyArray : Identifier.Substring(index + 1).Split(KeySeparator);
} }
} }

View File

@@ -1,16 +1,10 @@
using System; using System;
using System.Text; using System.Text;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Bridge;
using TweetDuck.Data;
using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetLib.Core.Features.Notifications{
sealed class TweetNotification{ public sealed class DesktopNotification{
private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>"; private const string DefaultHeadLayout = @"<html class=""scroll-v os-windows dark txt-size--14"" lang=""en-US"" id=""tduck"" data-td-font=""medium"" data-td-theme=""dark""><head><meta charset=""utf-8""><link href=""https://ton.twimg.com/tweetdeck-web/web/dist/bundle.4b1f87e09d.css"" rel=""stylesheet""><style type='text/css'>body { background: rgb(34, 36, 38) !important }</style>";
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
public enum Position{ public enum Position{
TopLeft, TopRight, BottomLeft, BottomRight, Custom TopLeft, TopRight, BottomLeft, BottomRight, Custom
} }
@@ -29,7 +23,7 @@ namespace TweetDuck.Core.Notification{
private readonly string html; private readonly string html;
private readonly int characters; private readonly int characters;
public TweetNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl){ public DesktopNotification(string columnId, string chirpId, string title, string html, int characters, string tweetUrl, string quoteUrl){
this.ColumnId = columnId; this.ColumnId = columnId;
this.ChirpId = chirpId; this.ChirpId = chirpId;
@@ -42,21 +36,22 @@ namespace TweetDuck.Core.Notification{
} }
public int GetDisplayDuration(int value){ public int GetDisplayDuration(int value){
return 2000+Math.Max(1000, value*characters); return 2000 + Math.Max(1000, value * characters);
} }
public string GenerateHtml(string bodyClasses, Control sync){ public string GenerateHtml(string bodyClasses, string? headLayout, string? customStyles){ // TODO
string headLayout = TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout; headLayout ??= DefaultHeadLayout;
string mainCSS = ScriptLoader.LoadResource("styles/notification.css", sync) ?? string.Empty; customStyles ??= string.Empty;
string customCSS = Program.Config.User.CustomNotificationCSS ?? string.Empty;
string mainCSS = App.ResourceHandler.Load("styles/notification.css") ?? string.Empty;
StringBuilder build = new StringBuilder(320 + headLayout.Length + mainCSS.Length + customCSS.Length + html.Length); StringBuilder build = new StringBuilder(320 + headLayout.Length + mainCSS.Length + customStyles.Length + html.Length);
build.Append("<!DOCTYPE html>"); build.Append("<!DOCTYPE html>");
build.Append(headLayout); build.Append(headLayout);
build.Append("<style type='text/css'>").Append(mainCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(mainCSS).Append("</style>");
if (!string.IsNullOrWhiteSpace(customCSS)){ if (!string.IsNullOrWhiteSpace(customStyles)){
build.Append("<style type='text/css'>").Append(customCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(customStyles).Append("</style>");
} }
build.Append("</head><body class='scroll-styled-v"); build.Append("</head><body class='scroll-styled-v");

View File

@@ -2,23 +2,17 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace TweetLib.Core.Features.Plugins.Enums{ namespace TweetLib.Core.Features.Plugins.Enums{
[Flags]
public enum PluginEnvironment{ public enum PluginEnvironment{
None = 0, Browser,
Browser = 1, Notification
Notification = 2
} }
public static class PluginEnvironmentExtensions{ public static class PluginEnvironments{
public static IEnumerable<PluginEnvironment> Values { get; } = new PluginEnvironment[]{ public static IEnumerable<PluginEnvironment> All { get; } = new PluginEnvironment[]{
PluginEnvironment.Browser, PluginEnvironment.Browser,
PluginEnvironment.Notification PluginEnvironment.Notification
}; };
public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
return environment == PluginEnvironment.Browser;
}
public static string? GetPluginScriptFile(this PluginEnvironment environment){ public static string? GetPluginScriptFile(this PluginEnvironment environment){
return environment switch{ return environment switch{
PluginEnvironment.Browser => "browser.js", PluginEnvironment.Browser => "browser.js",

View File

@@ -6,8 +6,8 @@ namespace TweetLib.Core.Features.Plugins.Enums{
Official, Custom Official, Custom
} }
public static class PluginGroupExtensions{ public static class PluginGroups{
public static IEnumerable<PluginGroup> Values { get; } = new PluginGroup[]{ public static IEnumerable<PluginGroup> All { get; } = new PluginGroup[]{
PluginGroup.Official, PluginGroup.Official,
PluginGroup.Custom PluginGroup.Custom
}; };

View File

@@ -0,0 +1,12 @@
using System;
using TweetLib.Core.Browser;
namespace TweetLib.Core.Features.Plugins.Events{
public sealed class PluginDispatchEventArgs : EventArgs{
public IScriptExecutor Executor { get; }
public PluginDispatchEventArgs(IScriptExecutor executor){
this.Executor = executor;
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetLib.Core.Features.Plugins{
public interface IPluginDispatcher{
event EventHandler<PluginDispatchEventArgs> Ready;
void AttachBridge(string name, object bridge);
}
}

View File

@@ -1,14 +0,0 @@
using System;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetLib.Core.Features.Plugins{
public interface IPluginManager{
IPluginConfig Config { get; }
event EventHandler<PluginErrorEventArgs> Reloaded;
int GetTokenFromPlugin(Plugin plugin);
Plugin GetPluginFromToken(int token);
}
}

View File

@@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using TweetLib.Core.Features.Plugins.Enums; using TweetLib.Core.Features.Plugins.Enums;
namespace TweetLib.Core.Features.Plugins{ namespace TweetLib.Core.Features.Plugins{
@@ -8,7 +10,6 @@ namespace TweetLib.Core.Features.Plugins{
public string Identifier { get; } public string Identifier { get; }
public PluginGroup Group { get; } public PluginGroup Group { get; }
public PluginEnvironment Environments { get; }
public string Name { get; } public string Name { get; }
public string Description { get; } public string Description { get; }
@@ -39,14 +40,15 @@ namespace TweetLib.Core.Features.Plugins{
private readonly string pathRoot; private readonly string pathRoot;
private readonly string pathData; private readonly string pathData;
private readonly ISet<PluginEnvironment> environments;
private Plugin(PluginGroup group, string identifier, string pathRoot, string pathData, Builder builder){ private Plugin(PluginGroup group, string identifier, string pathRoot, string pathData, Builder builder){
this.pathRoot = pathRoot; this.pathRoot = pathRoot;
this.pathData = pathData; this.pathData = pathData;
this.environments = builder.Environments;
this.Group = group; this.Group = group;
this.Identifier = identifier; this.Identifier = identifier;
this.Environments = builder.Environments;
this.Name = builder.Name; this.Name = builder.Name;
this.Description = builder.Description; this.Description = builder.Description;
@@ -60,8 +62,12 @@ namespace TweetLib.Core.Features.Plugins{
this.CanRun = AppVersion >= RequiredVersion; this.CanRun = AppVersion >= RequiredVersion;
} }
public bool HasEnvironment(PluginEnvironment environment){
return environments.Contains(environment);
}
public string GetScriptPath(PluginEnvironment environment){ public string GetScriptPath(PluginEnvironment environment){
if (Environments.HasFlag(environment)){ if (environments.Contains(environment)){
string? file = environment.GetPluginScriptFile(); string? file = environment.GetPluginScriptFile();
return file != null ? Path.Combine(pathRoot, file) : string.Empty; return file != null ? Path.Combine(pathRoot, file) : string.Empty;
} }
@@ -133,7 +139,7 @@ namespace TweetLib.Core.Features.Plugins{
public string ConfigDefault { get; set; } = string.Empty; public string ConfigDefault { get; set; } = string.Empty;
public Version RequiredVersion { get; set; } = DefaultRequiredVersion; public Version RequiredVersion { get; set; } = DefaultRequiredVersion;
public PluginEnvironment Environments { get; private set; } = PluginEnvironment.None; public ISet<PluginEnvironment> Environments { get; } = new HashSet<PluginEnvironment>();
private readonly PluginGroup group; private readonly PluginGroup group;
private readonly string pathRoot; private readonly string pathRoot;
@@ -148,7 +154,7 @@ namespace TweetLib.Core.Features.Plugins{
} }
public void AddEnvironment(PluginEnvironment environment){ public void AddEnvironment(PluginEnvironment environment){
this.Environments |= environment; Environments.Add(environment);
} }
public Plugin BuildAndSetup(){ public Plugin BuildAndSetup(){
@@ -158,7 +164,7 @@ namespace TweetLib.Core.Features.Plugins{
throw new InvalidOperationException("Plugin is missing a name in the .meta file"); throw new InvalidOperationException("Plugin is missing a name in the .meta file");
} }
if (plugin.Environments == PluginEnvironment.None){ if (!PluginEnvironments.All.Any(plugin.HasEnvironment)){
throw new InvalidOperationException("Plugin has no script files"); throw new InvalidOperationException("Plugin has no script files");
} }

View File

@@ -11,29 +11,56 @@ using TweetLib.Core.Utils;
namespace TweetLib.Core.Features.Plugins{ namespace TweetLib.Core.Features.Plugins{
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
public sealed class PluginBridge{ internal sealed class PluginBridge{
private readonly IPluginManager manager; private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
private readonly Random rand = new Random();
private readonly FileCache fileCache = new FileCache(); private readonly FileCache fileCache = new FileCache();
private readonly TwoKeyDictionary<int, string, InjectedHTML> notificationInjections = new TwoKeyDictionary<int, string, InjectedHTML>(4, 1); private readonly TwoKeyDictionary<int, string, InjectedHTML> notificationInjections = new TwoKeyDictionary<int, string, InjectedHTML>(4, 1);
public IEnumerable<InjectedHTML> NotificationInjections => notificationInjections.InnerValues; internal IEnumerable<InjectedHTML> NotificationInjections => notificationInjections.InnerValues;
public ISet<Plugin> WithConfigureFunction { get; } = new HashSet<Plugin>(); internal ISet<Plugin> WithConfigureFunction { get; } = new HashSet<Plugin>();
public PluginBridge(IPluginManager manager){ public PluginBridge(PluginManager manager){
this.manager = manager; manager.Reloaded += manager_Reloaded;
this.manager.Reloaded += manager_Reloaded; manager.Config.PluginChangedState += Config_PluginChangedState;
this.manager.Config.PluginChangedState += Config_PluginChangedState; }
internal 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;
}
private Plugin? GetPluginFromToken(int token){
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
} }
// Event handlers // Event handlers
private void manager_Reloaded(object sender, PluginErrorEventArgs e){ private void manager_Reloaded(object sender, PluginErrorEventArgs e){
tokens.Clear();
fileCache.Clear(); fileCache.Clear();
} }
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){ private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
if (!e.IsEnabled){ if (!e.IsEnabled){
int token = manager.GetTokenFromPlugin(e.Plugin); int token = GetTokenFromPlugin(e.Plugin);
fileCache.Remove(token); fileCache.Remove(token);
notificationInjections.Remove(token); notificationInjections.Remove(token);
@@ -43,7 +70,7 @@ namespace TweetLib.Core.Features.Plugins{
// Utility methods // Utility methods
private string GetFullPathOrThrow(int token, PluginFolder folder, string path){ private string GetFullPathOrThrow(int token, PluginFolder folder, string path){
Plugin plugin = manager.GetPluginFromToken(token); Plugin? plugin = GetPluginFromToken(token);
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path); string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(folder, path);
if (fullPath.Length == 0){ if (fullPath.Length == 0){
@@ -116,7 +143,7 @@ namespace TweetLib.Core.Features.Plugins{
} }
public void SetConfigurable(int token){ public void SetConfigurable(int token){
Plugin plugin = manager.GetPluginFromToken(token); Plugin? plugin = GetPluginFromToken(token);
if (plugin != null){ if (plugin != null){
WithConfigureFunction.Add(plugin); WithConfigureFunction.Add(plugin);

View File

@@ -79,7 +79,7 @@ namespace TweetLib.Core.Features.Plugins{
} }
private static PluginEnvironment EnvironmentFromFileName(string file){ private static PluginEnvironment EnvironmentFromFileName(string file){
return PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)); return PluginEnvironments.All.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
} }
private static void SetProperty(Plugin.Builder builder, string tag, string value){ private static void SetProperty(Plugin.Builder builder, string tag, string value){

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TweetLib.Core.Browser;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins.Config;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetLib.Core.Features.Plugins{
public sealed class PluginManager{
public string PathCustomPlugins => Path.Combine(pluginFolder, PluginGroup.Custom.GetSubFolder());
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 pluginFolder;
private readonly string pluginDataFolder;
private readonly PluginBridge bridge;
private IScriptExecutor? browserExecutor;
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
public PluginManager(IPluginConfig config, string pluginFolder, string pluginDataFolder){
this.Config = config;
this.Config.PluginChangedState += Config_PluginChangedState;
this.pluginFolder = pluginFolder;
this.pluginDataFolder = pluginDataFolder;
this.bridge = new PluginBridge(this);
}
public void Register(PluginEnvironment environment, IPluginDispatcher dispatcher){
dispatcher.AttachBridge("$TDP", bridge);
dispatcher.Ready += (sender, args) => {
IScriptExecutor executor = args.Executor;
if (environment == PluginEnvironment.Browser){
browserExecutor = executor;
}
Execute(environment, executor);
};
}
public void Reload(){
plugins.Clear();
List<string> errors = new List<string>(1);
foreach(var result in PluginGroups.All.SelectMany(group => PluginLoader.AllInFolder(pluginFolder, pluginDataFolder, group))){
if (result.HasValue){
plugins.Add(result.Value);
}
else{
errors.Add(result.Exception.Message);
}
}
Reloaded?.Invoke(this, new PluginErrorEventArgs(errors));
}
private void Execute(PluginEnvironment environment, IScriptExecutor executor){
if (!plugins.Any(plugin => plugin.HasEnvironment(environment)) || !executor.RunFile($"plugins.{environment.GetPluginScriptFile()}")){
return;
}
bool includeDisabled = environment == PluginEnvironment.Browser;
if (includeDisabled){
executor.RunScript("gen:pluginconfig", PluginScriptGenerator.GenerateConfig(Config));
}
List<string> errors = 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){
errors.Add($"{plugin.Identifier} ({Path.GetFileName(path)}): {e.Message}");
continue;
}
executor.RunScript($"plugin:{plugin}", PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, bridge.GetTokenFromPlugin(plugin), environment));
}
Executed?.Invoke(this, new PluginErrorEventArgs(errors));
}
private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browserExecutor?.RunFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
public bool IsPluginConfigurable(Plugin plugin){
return plugin.HasConfig || bridge.WithConfigureFunction.Contains(plugin);
}
public void ConfigurePlugin(Plugin plugin){
if (bridge.WithConfigureFunction.Contains(plugin) && browserExecutor != null){
browserExecutor.RunFunction("TDPF_configurePlugin", plugin);
}
else if (plugin.HasConfig){
App.SystemHandler.OpenFileExplorer(File.Exists(plugin.ConfigPath) ? plugin.ConfigPath : plugin.GetPluginFolder(PluginFolder.Data));
}
}
}
}

View File

@@ -5,7 +5,7 @@ using TweetLib.Core.Utils;
namespace TweetLib.Core.Features.Twitter{ namespace TweetLib.Core.Features.Twitter{
public class ImageUrl{ public class ImageUrl{
private static readonly Regex RegexImageUrlParams = new Regex(@"(format|name)=(\w+)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex RegexImageUrlParams = new Regex(@"(format|name)=(\w+)", RegexOptions.IgnoreCase);
public static readonly string[] ValidExtensions = { public static readonly string[] ValidExtensions = {
".jpg", ".jpeg", ".png", ".gif" ".jpg", ".jpeg", ".png", ".gif"

View File

@@ -0,0 +1,57 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using TweetLib.Core.Utils;
namespace TweetLib.Core.Features.Twitter{
public static class TwitterUrls{
public const string TweetDeck = "https://tweetdeck.twitter.com";
private const string TwitterTrackingUrl = "t.co";
public static Regex RegexAccount { get; } = new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)/?$");
public static bool IsTweetDeck(string url){
return url.Contains("//tweetdeck.twitter.com/");
}
public static bool IsTwitter(string url){
return url.Contains("//twitter.com/") || url.Contains("//mobile.twitter.com/");
}
public static bool IsTwitterLogin2Factor(string url){
return url.Contains("//twitter.com/account/login_verification") || url.Contains("//mobile.twitter.com/account/login_verification");
}
public static string? GetFileNameFromUrl(string url){
return StringUtils.NullIfEmpty(Path.GetFileName(new Uri(url).AbsolutePath));
}
public static string GetMediaLink(string url, ImageQuality quality){
return ImageUrl.TryParse(url, out var obj) ? obj.WithQuality(quality) : url;
}
public static string? GetImageFileName(string url){
return GetFileNameFromUrl(ImageUrl.TryParse(url, out var obj) ? obj.WithNoQuality : url);
}
public enum UrlType{
Invalid, Tracking, Fine
}
public static UrlType Check(string url){
if (url.Contains("\"")){
return UrlType.Invalid;
}
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 ? UrlType.Tracking : UrlType.Fine;
}
}
return UrlType.Invalid;
}
}
}

View File

@@ -4,7 +4,7 @@ using System.Threading;
namespace TweetLib.Core{ namespace TweetLib.Core{
public static class Lib{ public static class Lib{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string VersionTag = "1.18.1"; public const string VersionTag = "1.18.3";
public static CultureInfo Culture { get; private set; } public static CultureInfo Culture { get; private set; }

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