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

Compare commits

...

98 Commits

Author SHA1 Message Date
8d1c07d6b2 Release 1.13 2018-02-12 18:48:42 +01:00
c32462cc9e Update TweetDeck color selectors in CSS for black theme 2018-02-12 18:23:16 +01:00
ec94ea3273 Refactor PluginManager to use ITweetDeckBrowser & do some cleanup 2018-02-12 15:26:21 +01:00
41acd8c15b Refactor UpdateHandler to reference ITweetDeckBrowser 2018-02-12 11:35:39 +01:00
155a79f2ec Add ITweetDeckBrowser for refactoring 2018-02-12 11:34:23 +01:00
9197cb9be6 Add support for 'Configure' button to edit-design plugin 2018-02-12 11:26:50 +01:00
03d50c847b Add 'Configure' button to plugins with a configure() method & close dialog afterwards 2018-02-12 10:40:00 +01:00
bf45c40365 Make analytics debugging easier & tweak Counter serialization 2018-02-12 06:13:08 +01:00
679e126194 Reset all analytics counters 2018-02-12 05:41:03 +01:00
50e39164bd Update and add analytics data points & increase report interval to 14 days 2018-02-11 20:01:57 +01:00
cb9f75e968 Refactor AnalyticsFile events and usage 2018-02-11 16:59:02 +01:00
aa7f6cc3b1 Fix loading spinner sometimes being visible before getting replaced 2018-02-10 23:20:34 +01:00
fe601aed41 Redesign favorite/retweet notifications to be more compact and show full text 2018-02-10 13:09:09 +01:00
2282a9df28 Move 'Show this thread' in desktop notifications above media/quotes & fix hover color w/ black theme 2018-02-10 08:29:59 +01:00
2b54627750 Tweak media size and margins in desktop notifications 2018-02-10 07:54:50 +01:00
16051a0d25 Forgot this 2018-02-10 07:13:56 +01:00
66d5f0d790 Refactor IResourceHandler usage 2018-02-10 07:07:11 +01:00
07d29207f0 Restore loading background color and spinner from before the TweetDeck update 2018-02-10 06:50:52 +01:00
a60be2afcc More Visual Studio shit 2018-02-07 21:58:21 +01:00
027f3ee253 Remove recently added follow notification 2018-02-07 03:22:50 +01:00
04774815e4 Fix bad padding in introduction modal 2018-02-07 03:20:58 +01:00
61a73c055b Fix weird alignment of stuff in notification columns 2018-02-07 00:39:05 +01:00
7731534ffc Save some space in edit-design plugin 2018-02-07 00:21:57 +01:00
ed7bf99610 Prevent dev tools from leaking info in all request headers 2018-02-06 21:10:29 +01:00
cbe4272556 Hide unused items in TweetDeck Settings modal (startup notifications, gif autoplay) 2018-02-06 20:43:18 +01:00
8f5e3dfdcc Merge pull request #203 from chylex/cefsharp64
Update CefSharp to 64 & re-enable mp3s in sound notifications
2018-02-06 18:40:23 +01:00
35500c51f1 Allow export/import/restoring system options & refactor Manage Options dialog 2018-02-06 18:35:36 +01:00
629f873bb2 Add a debugger trigger shortcut to debug plugin 2018-02-06 18:25:11 +01:00
a44cb884c4 Fix a crash when restarting after importing/resetting profile & refactor 2018-02-06 17:04:32 +01:00
d5ad1d0daa Fix loading spinners, and links in notifications when using black theme
Closes #202
2018-02-06 04:38:57 +01:00
61ae7e3b6a Fix 'Show this thread' being too close to media thumbnails in notifications 2018-02-06 04:37:45 +01:00
01583e424f Re-add mp3 support in sound notifications 2018-02-06 04:10:03 +01:00
5c0aa1b3da Update CefSharp to 64 (early build) 2018-02-06 04:06:18 +01:00
07391efa70 Fix more visual issues (remove DM reply button background w/ black theme) 2018-02-02 23:13:43 +01:00
b80f1bfc7c Fix more visual issues (remove disabled button border w/ black theme) 2018-02-02 22:31:01 +01:00
ad310db86c Fix more visual issues (profile modal w/ black theme, timeline input shadow) 2018-02-02 21:57:28 +01:00
4ce0122a29 Fix hover/click effects on buttons under reply input box 2018-02-02 19:15:44 +01:00
a8894f7054 Fix visual issues with search input and buttons 2018-02-02 18:01:11 +01:00
1d1515351b Release 1.12.5.1 2018-02-02 16:57:28 +01:00
2a9ddd4468 Fix edit-design modal, black theme quote border, and dark theme scrollbar color 2018-02-02 16:56:06 +01:00
0f9a944775 Square-ify border of reply box & fix notification background 2018-02-02 15:54:22 +01:00
34ee9ebd66 Release 1.12.5 2018-02-02 15:24:19 +01:00
43f632b555 Allow detecting custom edit-design themes in analytics 2018-02-02 15:19:25 +01:00
7cf3f1d32c Add option for the old dark theme in edit-design plugin 2018-02-02 14:59:33 +01:00
e51e87647e Remove unknown property error in FileSerializer & refactor reading 2018-02-02 13:49:10 +01:00
b8aae88b11 Fix broken Shift swap when selecting accounts after a recent TweetDeck update 2018-01-31 00:28:19 +01:00
d06e29db15 Get rid of string.Split in FileSerializer
string.Split is not suitable for potentially very large strings, so this
decently improves memory usage
2018-01-30 15:45:19 +01:00
62449450f3 Release 1.12.4 2018-01-29 08:40:38 +01:00
b290c94635 Fix a video player crash caused by not handling remote commands on UI thread 2018-01-29 08:20:33 +01:00
f909b887d9 Fix screenshot issues (wrong media size and margin, hide "Show this thread") 2018-01-28 21:48:57 +01:00
5cf4843212 Fix broken screenshots of tweets with just one line of text 2018-01-28 21:40:18 +01:00
b3d1e1bfac Fix example notification timer breaking on skip (forward mouse button or Enter) 2018-01-28 20:54:49 +01:00
df47499a28 Update example notification (username, avatar, improve text) 2018-01-28 20:50:18 +01:00
421475ec87 Add a notification and way to follow the new TweetDuck account 2018-01-28 20:22:13 +01:00
29d999b8eb Remove old data collection notification that was shown after updating 2018-01-28 20:05:50 +01:00
acacd9a5e5 Fix reversed button tab order in FormMessage 2018-01-28 19:39:12 +01:00
b81c26f93f Add an option to ignore tracking URL warnings (t.co) 2018-01-28 19:38:40 +01:00
00b212944c Implement top tier account bamboozle scheme 2018-01-28 19:12:32 +01:00
70ba006e4d Reorganize logo file resources and remove about.png 2018-01-26 17:44:41 +01:00
118e0cae62 Compress the logo in the About form 2018-01-26 17:36:15 +01:00
c003bb4e71 Add a way to display the TweetDuck logo in the browser 2018-01-26 17:32:54 +01:00
e9b2fa7603 Release 1.12.3.1 2018-01-26 15:58:17 +01:00
35afaa105d Fix text alignment in the Feedback tab in Options 2018-01-26 15:49:44 +01:00
2e300a7b8f Fix broken stylesheets in notifications after a recent TweetDeck update
Closes #199
2018-01-26 15:43:52 +01:00
f3f5b88550 Refactor resource handler related extension methods 2018-01-22 14:53:38 +01:00
22f491d98a Release 1.12.3 2018-01-22 07:13:50 +01:00
7908c8ebd9 Goddammit VS 2018-01-22 06:54:58 +01:00
e114a93714 Refactor and move BrowserCache, VideoPlayer, and ExportManager 2018-01-22 06:41:20 +01:00
931761600f Move and refactor browser list options a bit more again 2018-01-22 05:17:50 +01:00
e5b4b03e1a Meh 2018-01-21 09:11:40 +01:00
f1e8b3fbf0 Move option for custom program for opening links at the end for better accessibility 2018-01-21 06:33:50 +01:00
4d64243a07 Turn WindowsUtils.Browser fields into get-only properties 2018-01-21 04:25:06 +01:00
3422b4d4d6 Fix height, tab order, and recently broken scroll focus handling in Options 2018-01-21 03:29:11 +01:00
b170d529fd Add an option to disable smooth scrolling 2018-01-21 03:11:12 +01:00
83741db5aa Fix broken smooth & horizontal scrolling with cursor above columns
Closes #192
2018-01-21 01:18:59 +01:00
c4b2b3ab25 Add verbose error logging to video player & tweak Reporter.Log 2018-01-19 23:37:45 +01:00
676df44985 Fix dialog title inconsistencies 2018-01-19 22:29:53 +01:00
037adc6b5c Add a way to select a custom program for opening links
References #185
2018-01-19 20:08:48 +01:00
186d17dd98 Add an option to select an installed browser to open links in
Closes #185
2018-01-19 19:19:40 +01:00
ab9ff980ef Fix dragging twitter links over columns from some sources or w/ url parameters not working 2018-01-19 06:05:46 +01:00
f297cb2623 Add line escaping to FileSerializer for easier manual file editing 2018-01-18 20:37:29 +01:00
b53c672768 Refactor Program.ResetConfig & Program.RestartWithArgs 2018-01-18 10:58:58 +01:00
1a2b967749 Move Chromium data from LocalAppData/CEF to TweetDuck storage folder 2018-01-18 10:47:16 +01:00
6ba30c48cf Remove BrowserUtils.HeaderAcceptLanguage and use default value instead 2018-01-18 10:37:43 +01:00
1af9ee9ced Release 1.12.2 2018-01-17 16:19:28 +01:00
e50480aa35 Fix edit-design plugin modal labels changing margins with different themes 2018-01-16 22:55:21 +01:00
6943c7813f Fix hovering scrollbars not changing their color with edit-design plugin enabled 2018-01-16 22:54:53 +01:00
7c9b4382ca Fix Follow dialog closing when clicking any but the first Follow button 2018-01-16 19:20:14 +01:00
3187f97592 Rewrite 'Keep Like/Follow dialogs open' code after TD removed the old way 2018-01-15 21:25:43 +01:00
b71a367052 Merge pull request #196 from chylex/delet_audio_lib
Remove audio library
2018-01-14 11:13:31 +01:00
2d4bbf2a6f Fix sound notification extension detection and add warning to mp3 files 2018-01-14 00:08:43 +01:00
6e59dfddcc Remove audio library 2018-01-13 23:38:30 +01:00
bd92fc6ee0 Use <audio> for custom sound notifications & allow volume control for default one
Closes #195
2018-01-13 22:59:34 +01:00
2f61de7025 Add GetHandlerFactory extension method to BrowserUtils 2018-01-13 22:37:24 +01:00
8fcec7ec7c Merge remote-tracking branch 'refs/remotes/origin/master' into delet_audio_lib 2018-01-13 19:50:13 +01:00
33d9ba3871 Refactor UserConfig event invocations into a generic method 2018-01-13 19:49:16 +01:00
4f8c778ba0 Ignore errors in automatic cache clearing
Closes #194
2018-01-13 15:35:20 +01:00
804c739038 Fix broken element dragging (timeline tweets and maybe more) 2018-01-13 15:27:07 +01:00
96 changed files with 2594 additions and 1479 deletions

View File

@@ -4,9 +4,7 @@ using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class SystemConfig{ sealed class SystemConfig{
private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>{ private static readonly FileSerializer<SystemConfig> Serializer = new FileSerializer<SystemConfig>();
HandleUnknownProperties = FileSerializer<SystemConfig>.IgnoreProperties("EnableBrowserGCReload", "BrowserMemoryThreshold")
};
public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) && public static readonly bool IsHardwareAccelerationSupported = File.Exists(Path.Combine(Program.ProgramPath, "libEGL.dll")) &&
File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll")); File.Exists(Path.Combine(Program.ProgramPath, "libGLESv2.dll"));

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
@@ -10,9 +11,7 @@ using TweetDuck.Data.Serialization;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class UserConfig{ sealed class UserConfig{
private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>{ private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
HandleUnknownProperties = FileSerializer<UserConfig>.IgnoreProperties("AppLocale")
};
static UserConfig(){ static UserConfig(){
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
@@ -38,7 +37,6 @@ namespace TweetDuck.Configuration{
public bool FirstRun { get; set; } = true; public bool FirstRun { get; set; } = true;
public bool AllowDataCollection { get; set; } = false; public bool AllowDataCollection { get; set; } = false;
public bool ShowDataCollectionNotification { get; set; } = true;
public WindowState BrowserWindow { get; set; } = new WindowState(); public WindowState BrowserWindow { get; set; } = new WindowState();
public WindowState PluginsWindow { get; set; } = new WindowState(); public WindowState PluginsWindow { get; set; } = new WindowState();
@@ -49,10 +47,15 @@ namespace TweetDuck.Configuration{
public bool KeepLikeFollowDialogsOpen { get; set; } = true; public bool KeepLikeFollowDialogsOpen { get; set; } = true;
public bool BestImageQuality { get; set; } = true; public bool BestImageQuality { get; set; } = true;
public bool EnableAnimatedImages { get; set; } = true; public bool EnableAnimatedImages { get; set; } = true;
public int VideoPlayerVolume { get; set; } = 50;
public bool IgnoreTrackingUrlWarning { get; set; } = false;
public bool EnableSmoothScrolling { get; set; } = true;
public string BrowserPath { get; set; } = null;
private int _zoomLevel = 100; private int _zoomLevel = 100;
private bool _muteNotifications; private bool _muteNotifications;
public int VideoPlayerVolume { get; set; } = 50;
public bool EnableSpellCheck { get; set; } = false; public bool EnableSpellCheck { get; set; } = false;
public string SpellCheckLanguage { get; set; } = "en-US"; public string SpellCheckLanguage { get; set; } = "en-US";
public string TranslationTarget { get; set; } = "en"; public string TranslationTarget { get; set; } = "en";
@@ -82,8 +85,8 @@ namespace TweetDuck.Configuration{
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;
public int NotificationSoundVolume { get; set; } = 100;
private string _notificationSoundPath; private string _notificationSoundPath;
private int _notificationSoundVolume = 100;
public string CustomCefArgs { get; set; } = null; public string CustomCefArgs { get; set; } = null;
public string CustomBrowserCSS { get; set; } = null; public string CustomBrowserCSS { get; set; } = null;
@@ -93,45 +96,33 @@ namespace TweetDuck.Configuration{
public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation; public bool IsCustomNotificationPositionSet => CustomNotificationPosition != ControlExtensions.InvisibleLocation;
public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty; public bool IsCustomNotificationSizeSet => CustomNotificationSize != Size.Empty;
public bool IsCustomSoundNotificationSet => NotificationSoundPath != string.Empty;
public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default; public TwitterUtils.ImageQuality TwitterImageQuality => BestImageQuality ? TwitterUtils.ImageQuality.Orig : TwitterUtils.ImageQuality.Default;
public string NotificationSoundPath{ public string NotificationSoundPath{
get => string.IsNullOrEmpty(_notificationSoundPath) ? string.Empty : _notificationSoundPath; get => _notificationSoundPath ?? string.Empty;
set => _notificationSoundPath = value; set => UpdatePropertyWithEvent(ref _notificationSoundPath, value, SoundNotificationChanged);
}
public int NotificationSoundVolume{
get => _notificationSoundVolume;
set => UpdatePropertyWithEvent(ref _notificationSoundVolume, value, SoundNotificationChanged);
} }
public bool MuteNotifications{ public bool MuteNotifications{
get => _muteNotifications; get => _muteNotifications;
set => UpdatePropertyWithEvent(ref _muteNotifications, value, MuteToggled);
set{
if (_muteNotifications != value){
_muteNotifications = value;
MuteToggled?.Invoke(this, EventArgs.Empty);
}
}
} }
public int ZoomLevel{ public int ZoomLevel{
get => _zoomLevel; get => _zoomLevel;
set => UpdatePropertyWithEvent(ref _zoomLevel, value, ZoomLevelChanged);
set{
if (_zoomLevel != value){
_zoomLevel = value;
ZoomLevelChanged?.Invoke(this, EventArgs.Empty);
}
}
} }
public TrayIcon.Behavior TrayBehavior{ public TrayIcon.Behavior TrayBehavior{
get => _trayBehavior; get => _trayBehavior;
set => UpdatePropertyWithEvent(ref _trayBehavior, value, TrayBehaviorChanged);
set{
if (_trayBehavior != value){
_trayBehavior = value;
TrayBehaviorChanged?.Invoke(this, EventArgs.Empty);
}
}
} }
// EVENTS // EVENTS
@@ -139,6 +130,7 @@ namespace TweetDuck.Configuration{
public event EventHandler MuteToggled; public event EventHandler MuteToggled;
public event EventHandler ZoomLevelChanged; public event EventHandler ZoomLevelChanged;
public event EventHandler TrayBehaviorChanged; public event EventHandler TrayBehaviorChanged;
public event EventHandler SoundNotificationChanged;
// END OF CONFIG // END OF CONFIG
@@ -148,6 +140,13 @@ namespace TweetDuck.Configuration{
this.file = file; this.file = file;
} }
private void UpdatePropertyWithEvent<T>(ref T field, T value, EventHandler eventHandler){
if (!EqualityComparer<T>.Default.Equals(field, value)){
field = value;
eventHandler?.Invoke(this, EventArgs.Empty);
}
}
public void Save(){ public void Save(){
try{ try{
if (File.Exists(file)){ if (File.Exists(file)){
@@ -177,6 +176,18 @@ namespace TweetDuck.Configuration{
} }
} }
public void Reset(){
try{
File.Delete(file);
File.Delete(GetBackupFile(file));
}catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not delete configuration files to reset the options.", true, e);
return;
}
Reload();
}
private void LoadInternal(bool backup){ private void LoadInternal(bool backup){
Serializer.Read(backup ? GetBackupFile(file) : file, this); Serializer.Read(backup ? GetBackupFile(file) : file, this);

View File

@@ -19,7 +19,6 @@ namespace TweetDuck.Core.Bridge{
build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn)); build.Append("x.openSearchInFirstColumn=").Append(Bool(Program.UserConfig.OpenSearchInFirstColumn));
build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen)); build.Append("x.keepLikeFollowDialogsOpen=").Append(Bool(Program.UserConfig.KeepLikeFollowDialogsOpen));
build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications)); build.Append("x.muteNotifications=").Append(Bool(Program.UserConfig.MuteNotifications));
build.Append("x.hasCustomNotificationSound=").Append(Bool(Program.UserConfig.NotificationSoundPath.Length > 0));
build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews)); build.Append("x.notificationMediaPreviews=").Append(Bool(Program.UserConfig.NotificationMediaPreviews));
build.Append("x.translationTarget=").Append(Str(Program.UserConfig.TranslationTarget)); build.Append("x.translationTarget=").Append(Str(Program.UserConfig.TranslationTarget));
} }

View File

@@ -126,7 +126,7 @@ namespace TweetDuck.Core.Bridge{
public void OnTweetSound(){ public void OnTweetSound(){
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
form.OnTweetNotification(); form.OnTweetNotification();
form.PlayNotificationSound(); form.OnTweetSound();
}); });
} }

View File

@@ -5,20 +5,18 @@ using TweetDuck.Configuration;
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.Management;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot; using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Analytics; using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Management;
using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Audio;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed partial class FormBrowser : Form{ sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{
private static UserConfig Config => Program.UserConfig; private static UserConfig Config => Program.UserConfig;
public bool IsWaiting{ public bool IsWaiting{
@@ -40,6 +38,8 @@ namespace TweetDuck.Core{
public string UpdateInstallerPath { get; private set; } public string UpdateInstallerPath { get; private set; }
public AnalyticsFile AnalyticsFile => analytics?.File ?? AnalyticsFile.Dummy;
private readonly TweetDeckBrowser browser; private readonly TweetDeckBrowser browser;
private readonly PluginManager plugins; private readonly PluginManager plugins;
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
@@ -50,7 +50,6 @@ namespace TweetDuck.Core{
private FormWindowState prevState; private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager; private TweetScreenshotManager notificationScreenshotManager;
private SoundNotification soundNotification;
private VideoPlayer videoPlayer; private VideoPlayer videoPlayer;
private AnalyticsManager analytics; private AnalyticsManager analytics;
@@ -59,7 +58,10 @@ namespace TweetDuck.Core{
Text = Program.BrandName; Text = Program.BrandName;
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath); this.browser = new TweetDeckBrowser(this, new TweetDeckBridge.Browser(this, notification));
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.plugins = new PluginManager(browser, Program.PluginPath, Program.PluginConfigFilePath);
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();
@@ -67,11 +69,6 @@ namespace TweetDuck.Core{
this.notification = new FormNotificationTweet(this, plugins); this.notification = new FormNotificationTweet(this, plugins);
this.notification.Show(); this.notification.Show();
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge.Browser(this, notification));
this.browser.PageLoaded += browser_PageLoaded;
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
Disposed += (sender, args) => { Disposed += (sender, args) => {
@@ -82,7 +79,6 @@ namespace TweetDuck.Core{
contextMenu.Dispose(); contextMenu.Dispose();
notificationScreenshotManager?.Dispose(); notificationScreenshotManager?.Dispose();
soundNotification?.Dispose();
videoPlayer?.Dispose(); videoPlayer?.Dispose();
analytics?.Dispose(); analytics?.Dispose();
}; };
@@ -95,7 +91,7 @@ namespace TweetDuck.Core{
UpdateTrayIcon(); UpdateTrayIcon();
this.updates = browser.CreateUpdateHandler(updaterSettings); this.updates = new UpdateHandler(browser, updaterSettings);
this.updates.UpdateAccepted += updates_UpdateAccepted; this.updates.UpdateAccepted += updates_UpdateAccepted;
this.updates.UpdateDismissed += updates_UpdateDismissed; this.updates.UpdateDismissed += updates_UpdateDismissed;
@@ -199,7 +195,7 @@ namespace TweetDuck.Core{
} }
private void Config_MuteToggled(object sender, EventArgs e){ private void Config_MuteToggled(object sender, EventArgs e){
TriggerAnalyticsEvent(AnalyticsFile.Event.MuteNotification); AnalyticsFile.NotificationMutes.Trigger();
} }
private void Config_TrayBehaviorChanged(object sender, EventArgs e){ private void Config_TrayBehaviorChanged(object sender, EventArgs e){
@@ -217,45 +213,13 @@ namespace TweetDuck.Core{
ForceClose(); ForceClose();
} }
private void browser_PageLoaded(object sender, EventArgs e){
if (Config.ShowDataCollectionNotification){
this.InvokeAsyncSafe(() => {
if (!Config.FirstRun && Config.AllowDataCollection){
FormMessage form = new FormMessage("Anonymous Data Update", "Hi! You can now review your anonymous data report, and opt-out if you've changed your mind. Collected data will be used to focus development on most commonly used features. If you want to opt-out but still support the project, any feedback and donations are appreciated.", MessageBoxIcon.Information);
form.AddButton("OK", ControlType.Accept | ControlType.Focused);
Button btnReviewSettings = new Button{
Anchor = AnchorStyles.Bottom | AnchorStyles.Left,
Font = SystemFonts.MessageBoxFont,
Location = new Point(9, 12),
Margin = new Padding(0, 0, 48, 0),
Size = new Size(160, 26),
Text = "Review Feedback Options",
UseVisualStyleBackColor = true
};
btnReviewSettings.Click += (sender2, args2) => {
form.Close();
OpenSettings(typeof(TabSettingsFeedback));
};
form.AddActionControl(btnReviewSettings);
ShowChildForm(form);
}
Config.ShowDataCollectionNotification = false;
Config.Save();
});
}
}
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); 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){
ReloadToTweetDeck(); browser.ReloadToTweetDeck();
} }
} }
@@ -291,22 +255,6 @@ namespace TweetDuck.Core{
}); });
} }
private void soundNotification_PlaybackError(object sender, PlaybackErrorEventArgs e){
e.Ignore = true;
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, MessageBoxIcon.Error)){
form.AddButton(FormMessage.Ignore, ControlType.Cancel | ControlType.Focused);
Button btnOpenSettings = form.AddButton("View Options");
btnOpenSettings.Width += 16;
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
OpenSettings(typeof(TabSettingsSounds));
}
}
}
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()){ if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
@@ -322,7 +270,7 @@ namespace TweetDuck.Core{
} }
else{ else{
browser.OnMouseClickExtra(m.WParam); browser.OnMouseClickExtra(m.WParam);
TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton); AnalyticsFile.BrowserExtraMouseButtons.Trigger();
} }
return; return;
@@ -347,6 +295,7 @@ namespace TweetDuck.Core{
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ReloadToTweetDeck(); browser.ReloadToTweetDeck();
AnalyticsFile.BrowserReloads.Trigger();
} }
public void TriggerTweetScreenshot(){ public void TriggerTweetScreenshot(){
@@ -357,12 +306,13 @@ namespace TweetDuck.Core{
browser.ReloadColumns(); browser.ReloadColumns();
} }
public void ApplyROT13(){ public void PlaySoundNotification(){
browser.ApplyROT13(); browser.PlaySoundNotification();
} }
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){ public void ApplyROT13(){
analytics?.TriggerEvent(e); browser.ApplyROT13();
AnalyticsFile.UsedROT13.Trigger();
} }
// callback handlers // callback handlers
@@ -371,7 +321,6 @@ namespace TweetDuck.Core{
if (Config.FirstRun){ if (Config.FirstRun){
Config.FirstRun = false; Config.FirstRun = false;
Config.AllowDataCollection = allowDataCollection; Config.AllowDataCollection = allowDataCollection;
Config.ShowDataCollectionNotification = false;
Config.Save(); Config.Save();
if (allowDataCollection && analytics == null){ if (allowDataCollection && analytics == null){
@@ -434,21 +383,21 @@ namespace TweetDuck.Core{
form.Dispose(); form.Dispose();
}; };
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenOptions); AnalyticsFile.OpenOptions.Trigger();
ShowChildForm(form); ShowChildForm(form);
} }
} }
public void OpenAbout(){ public void OpenAbout(){
if (!FormManager.TryBringToFront<FormAbout>()){ if (!FormManager.TryBringToFront<FormAbout>()){
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout); AnalyticsFile.OpenAbout.Trigger();
ShowChildForm(new FormAbout()); ShowChildForm(new FormAbout());
} }
} }
public void OpenPlugins(){ public void OpenPlugins(){
if (!FormManager.TryBringToFront<FormPlugins>()){ if (!FormManager.TryBringToFront<FormPlugins>()){
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins); AnalyticsFile.OpenPlugins.Trigger();
ShowChildForm(new FormPlugins(plugins)); ShowChildForm(new FormPlugins(plugins));
} }
} }
@@ -459,20 +408,8 @@ namespace TweetDuck.Core{
} }
} }
public void PlayNotificationSound(){ public void OnTweetSound(){
if (Config.NotificationSoundPath.Length == 0){ AnalyticsFile.SoundNotifications.Trigger();
return;
}
if (soundNotification == null){
soundNotification = new SoundNotification();
soundNotification.PlaybackError += soundNotification_PlaybackError;
}
soundNotification.SetVolume(Config.NotificationSoundVolume);
soundNotification.Play(Config.NotificationSoundPath);
TriggerAnalyticsEvent(AnalyticsFile.Event.SoundNotification);
} }
public void PlayVideo(string url, string username){ public void PlayVideo(string url, string username){
@@ -490,7 +427,7 @@ namespace TweetDuck.Core{
} }
videoPlayer.Launch(url, username); videoPlayer.Launch(url, username);
TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay); AnalyticsFile.VideoPlays.Trigger();
} }
public bool ProcessBrowserKey(Keys key){ public bool ProcessBrowserKey(Keys key){
@@ -512,7 +449,7 @@ namespace TweetDuck.Core{
notification.FinishCurrentNotification(); notification.FinishCurrentNotification();
browser.ShowTweetDetail(columnId, chirpId, fallbackUrl); browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetDetail); AnalyticsFile.TweetDetails.Trigger();
} }
public void OnTweetScreenshotReady(string html, int width, int height){ public void OnTweetScreenshotReady(string html, int width, int height){
@@ -521,7 +458,7 @@ namespace TweetDuck.Core{
} }
notificationScreenshotManager.Trigger(html, width, height); notificationScreenshotManager.Trigger(html, width, height);
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetScreenshot); AnalyticsFile.TweetScreenshots.Trigger();
} }
public void DisplayTooltip(string text){ public void DisplayTooltip(string text){

View File

@@ -8,7 +8,10 @@ using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using TweetDuck.Core.Management;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
@@ -42,6 +45,12 @@ namespace TweetDuck.Core.Handling{
private string[] lastHighlightedTweetAuthors; private string[] lastHighlightedTweetAuthors;
private string[] lastHighlightedTweetImageList; private string[] lastHighlightedTweetImageList;
private readonly AnalyticsFile.IProvider analytics;
protected ContextMenuBase(AnalyticsFile.IProvider analytics){
this.analytics = analytics;
}
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
lastHighlightedTweetAuthors = StringUtils.EmptyArray; lastHighlightedTweetAuthors = StringUtils.EmptyArray;
@@ -80,7 +89,7 @@ namespace TweetDuck.Core.Handling{
model.AddItem(MenuSaveMedia, TextSave("video")); model.AddItem(MenuSaveMedia, TextSave("video"));
model.AddSeparator(); model.AddSeparator();
} }
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){ else if (((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage) && parameters.SourceUrl != TweetNotification.AppLogo.Url){
model.AddItem(MenuViewImage, "View image in photo viewer"); model.AddItem(MenuViewImage, "View image in photo viewer");
model.AddItem(MenuOpenMediaUrl, TextOpen("image")); model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
model.AddItem(MenuCopyMediaUrl, TextCopy("image")); model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
@@ -95,26 +104,29 @@ namespace TweetDuck.Core.Handling{
} }
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
Control control = browserControl.AsControl();
switch(commandId){ switch(commandId){
case MenuOpenLinkUrl: case MenuOpenLinkUrl:
OpenBrowser(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.LinkUrl); OpenBrowser(control, IsLink ? ContextInfo.Value : parameters.LinkUrl);
break; break;
case MenuCopyLinkUrl: case MenuCopyLinkUrl:
SetClipboardText(browserControl.AsControl(), IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl); SetClipboardText(control, IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
break; break;
case MenuCopyUsername: case MenuCopyUsername:
Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl); Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl);
SetClipboardText(browserControl.AsControl(), match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl); SetClipboardText(control, match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl);
control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger);
break; break;
case MenuOpenMediaUrl: case MenuOpenMediaUrl:
OpenBrowser(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality)); OpenBrowser(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
break; break;
case MenuCopyMediaUrl: case MenuCopyMediaUrl:
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality)); SetClipboardText(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
break; break;
case MenuViewImage: case MenuViewImage:
@@ -136,6 +148,8 @@ namespace TweetDuck.Core.Handling{
ViewFile(); ViewFile();
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger);
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => { BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, 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);
}); });
@@ -145,15 +159,18 @@ namespace TweetDuck.Core.Handling{
case MenuSaveMedia: case MenuSaveMedia:
if (IsVideo){ if (IsVideo){
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault()); TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
} }
else{ else{
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality); TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
} }
break; break;
case MenuSaveTweetImages: case MenuSaveTweetImages:
control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality); TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
break; break;

View File

@@ -2,7 +2,6 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
@@ -31,7 +30,7 @@ namespace TweetDuck.Core.Handling{
private string lastHighlightedTweetUrl; private string lastHighlightedTweetUrl;
private string lastHighlightedQuoteUrl; private string lastHighlightedQuoteUrl;
public ContextMenuBrowser(FormBrowser form){ public ContextMenuBrowser(FormBrowser form) : base(form){
this.form = form; this.form = form;
} }
@@ -97,7 +96,7 @@ namespace TweetDuck.Core.Handling{
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
form.InvokeAsyncSafe(() => form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu)); form.InvokeAsyncSafe(form.AnalyticsFile.BrowserContextMenus.Trigger);
} }
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
@@ -166,7 +165,7 @@ namespace TweetDuck.Core.Handling{
menu.Popup += (sender, args) => { menu.Popup += (sender, args) => {
menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications; menu.MenuItems[1].Checked = Program.UserConfig.MuteNotifications;
form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu); form.AnalyticsFile.BrowserContextMenus.Trigger();
}; };
return menu; return menu;

View File

@@ -1,7 +1,10 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class ContextMenuGuide : ContextMenuBase{ sealed class ContextMenuGuide : ContextMenuBase{
public ContextMenuGuide(AnalyticsFile.IProvider analytics) : base(analytics){}
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear(); model.Clear();
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);

View File

@@ -1,7 +1,6 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class ContextMenuNotification : ContextMenuBase{ sealed class ContextMenuNotification : ContextMenuBase{
@@ -14,7 +13,7 @@ namespace TweetDuck.Core.Handling{
private readonly FormNotificationBase form; private readonly FormNotificationBase form;
private readonly bool enableCustomMenu; private readonly bool enableCustomMenu;
public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu){ public ContextMenuNotification(FormNotificationBase form, bool enableCustomMenu) : base(form){
this.form = form; this.form = form;
this.enableCustomMenu = enableCustomMenu; this.enableCustomMenu = enableCustomMenu;
} }
@@ -57,7 +56,7 @@ namespace TweetDuck.Core.Handling{
form.InvokeAsyncSafe(() => { form.InvokeAsyncSafe(() => {
form.ContextMenuOpen = true; form.ContextMenuOpen = true;
form.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationContextMenu); form.AnalyticsFile.NotificationContextMenus.Trigger();
}); });
} }

View File

@@ -2,7 +2,6 @@
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Handling { namespace TweetDuck.Core.Handling {
sealed class KeyboardHandlerNotification : IKeyboardHandler{ sealed class KeyboardHandlerNotification : IKeyboardHandler{
@@ -13,7 +12,7 @@ namespace TweetDuck.Core.Handling {
} }
private void TriggerKeyboardShortcutAnalytics(){ private void TriggerKeyboardShortcutAnalytics(){
notification.InvokeAsyncSafe(() => notification.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationKeyboardShortcut)); notification.InvokeAsyncSafe(notification.AnalyticsFile.NotificationKeyboardShortcuts.Trigger);
} }
bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){ bool IKeyboardHandler.OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut){

View File

@@ -1,4 +1,5 @@
using CefSharp; using System.Collections.Specialized;
using CefSharp;
using CefSharp.Handler; using CefSharp.Handler;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
@@ -7,5 +8,15 @@ namespace TweetDuck.Core.Handling{
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){ public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl); return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
} }
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
if (ContextMenuBase.HasDevTools){
NameValueCollection headers = request.Headers;
headers.Remove("x-devtools-emulate-network-conditions-client-id");
request.Headers = headers;
}
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
}
} }
} }

View File

@@ -1,4 +1,5 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class RequestHandlerBrowser : RequestHandlerBase{ sealed class RequestHandlerBrowser : RequestHandlerBase{
@@ -8,10 +9,20 @@ 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.Script && request.Url.Contains("analytics.")){ if (request.ResourceType == ResourceType.Script && request.Url.Contains("analytics.")){
callback.Dispose();
return CefReturnValue.Cancel; return CefReturnValue.Cancel;
} }
return CefReturnValue.Continue; return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
}
public override bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
if (request.ResourceType == ResourceType.Image && request.Url.Contains("backgrounds/spinner_blue")){
request.Url = TwitterUtils.LoadingSpinner.Url;
return true;
}
return base.OnResourceResponse(browserControl, browser, frame, request, response);
} }
} }
} }

10
Core/ITweetDeckBrowser.cs Normal file
View File

@@ -0,0 +1,10 @@
using System;
using CefSharp;
namespace TweetDuck.Core{
interface ITweetDeckBrowser{
void RegisterBridge(string name, object obj);
void OnFrameLoaded(Action<IFrame> callback);
void ExecuteFunction(string name, params object[] args);
}
}

View File

@@ -1,10 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Management{
static class BrowserCache{ static class BrowserCache{
public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache"); public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache");
@@ -36,9 +36,15 @@ namespace TweetDuck.Core.Utils{
} }
else if (shouldRun && AutoClearTimer == null){ else if (shouldRun && AutoClearTimer == null){
AutoClearTimer = new Timer(state => { AutoClearTimer = new Timer(state => {
if (AutoClearTimer != null && CalculateCacheSize() >= Program.SystemConfig.ClearCacheThreshold*1024L*1024L){ if (AutoClearTimer != null){
try{
if (CalculateCacheSize() >= Program.SystemConfig.ClearCacheThreshold*1024L*1024L){
SetClearOnExit(); SetClearOnExit();
} }
}catch(Exception){
// TODO should probably log errors and report them at some point
}
}
}, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(4)); }, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(4));
} }
} }

View File

@@ -2,38 +2,49 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using TweetDuck.Core.Other;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
namespace TweetDuck.Core.Other.Settings.Export{ namespace TweetDuck.Core.Management{
sealed class ExportManager{ sealed class ProfileManager{
private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies"); private static readonly string CookiesPath = Path.Combine(Program.StoragePath, "Cookies");
private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp"); private static readonly string TempCookiesPath = Path.Combine(Program.StoragePath, "CookiesTmp");
[Flags]
public enum Items{
None = 0,
UserConfig = 1,
SystemConfig = 2,
Session = 4,
PluginData = 8,
All = UserConfig|SystemConfig|Session|PluginData
}
public bool IsRestarting { get; private set; } public bool IsRestarting { get; private set; }
public Exception LastException { get; private set; } public Exception LastException { get; private set; }
private readonly string file; private readonly string file;
private readonly PluginManager plugins; private readonly PluginManager plugins;
public ExportManager(string file, PluginManager plugins){ public ProfileManager(string file, PluginManager plugins){
this.file = file; this.file = file;
this.plugins = plugins; this.plugins = plugins;
} }
public bool Export(ExportFileFlags flags){ public bool Export(Items items){
try{ try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
if (flags.HasFlag(ExportFileFlags.UserConfig)){ if (items.HasFlag(Items.UserConfig)){
stream.WriteFile("config", Program.UserConfigFilePath); stream.WriteFile("config", Program.UserConfigFilePath);
} }
if (flags.HasFlag(ExportFileFlags.SystemConfig)){ if (items.HasFlag(Items.SystemConfig)){
stream.WriteFile("system", Program.SystemConfigFilePath); stream.WriteFile("system", Program.SystemConfigFilePath);
} }
if (flags.HasFlag(ExportFileFlags.PluginData)){ if (items.HasFlag(Items.PluginData)){
stream.WriteFile("plugin.config", Program.PluginConfigFilePath); stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
foreach(Plugin plugin in plugins.Plugins){ foreach(Plugin plugin in plugins.Plugins){
@@ -47,7 +58,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
} }
} }
if (flags.HasFlag(ExportFileFlags.Session)){ if (items.HasFlag(Items.Session)){
stream.WriteFile("cookies", CookiesPath); stream.WriteFile("cookies", CookiesPath);
} }
@@ -61,8 +72,8 @@ namespace TweetDuck.Core.Other.Settings.Export{
} }
} }
public ExportFileFlags GetImportFlags(){ public Items FindImportItems(){
ExportFileFlags flags = ExportFileFlags.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))){
@@ -71,33 +82,33 @@ namespace TweetDuck.Core.Other.Settings.Export{
while((key = stream.SkipFile()) != null){ while((key = stream.SkipFile()) != null){
switch(key){ switch(key){
case "config": case "config":
flags |= ExportFileFlags.UserConfig; items |= Items.UserConfig;
break; break;
case "system": case "system":
flags |= ExportFileFlags.SystemConfig; items |= Items.SystemConfig;
break; break;
case "plugin.config": case "plugin.config":
case "plugin.data": case "plugin.data":
flags |= ExportFileFlags.PluginData; items |= Items.PluginData;
break; break;
case "cookies": case "cookies":
flags |= ExportFileFlags.Session; items |= Items.Session;
break; break;
} }
} }
} }
}catch(Exception e){ }catch(Exception e){
LastException = e; LastException = e;
flags = ExportFileFlags.None; items = Items.None;
} }
return flags; return items;
} }
public bool Import(ExportFileFlags flags){ public bool Import(Items items){
try{ try{
HashSet<string> missingPlugins = new HashSet<string>(); HashSet<string> missingPlugins = new HashSet<string>();
@@ -107,14 +118,14 @@ namespace TweetDuck.Core.Other.Settings.Export{
while((entry = stream.ReadFile()) != null){ while((entry = stream.ReadFile()) != null){
switch(entry.KeyName){ switch(entry.KeyName){
case "config": case "config":
if (flags.HasFlag(ExportFileFlags.UserConfig)){ if (items.HasFlag(Items.UserConfig)){
entry.WriteToFile(Program.UserConfigFilePath); entry.WriteToFile(Program.UserConfigFilePath);
} }
break; break;
case "system": case "system":
if (flags.HasFlag(ExportFileFlags.SystemConfig)){ if (items.HasFlag(Items.SystemConfig)){
entry.WriteToFile(Program.SystemConfigFilePath); entry.WriteToFile(Program.SystemConfigFilePath);
IsRestarting = true; IsRestarting = true;
} }
@@ -122,14 +133,14 @@ namespace TweetDuck.Core.Other.Settings.Export{
break; break;
case "plugin.config": case "plugin.config":
if (flags.HasFlag(ExportFileFlags.PluginData)){ if (items.HasFlag(Items.PluginData)){
entry.WriteToFile(Program.PluginConfigFilePath); entry.WriteToFile(Program.PluginConfigFilePath);
} }
break; break;
case "plugin.data": case "plugin.data":
if (flags.HasFlag(ExportFileFlags.PluginData)){ if (items.HasFlag(Items.PluginData)){
string[] value = entry.KeyValue; string[] value = entry.KeyValue;
entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true); entry.WriteToFile(Path.Combine(Program.PluginDataPath, value[0], value[1]), true);
@@ -142,7 +153,7 @@ namespace TweetDuck.Core.Other.Settings.Export{
break; break;
case "cookies": case "cookies":
if (flags.HasFlag(ExportFileFlags.Session)){ if (items.HasFlag(Items.Session)){
entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath)); entry.WriteToFile(Path.Combine(Program.StoragePath, TempCookiesPath));
IsRestarting = true; IsRestarting = true;
} }
@@ -199,4 +210,10 @@ namespace TweetDuck.Core.Other.Settings.Export{
public string Relative { get; set; } public string Relative { get; set; }
} }
} }
static class ProfileManagerExtensions{
public static bool NeedsRestart(this ProfileManager.Items items){
return items.HasFlag(ProfileManager.Items.SystemConfig) || items.HasFlag(ProfileManager.Items.Session);
}
}
} }

View File

@@ -3,10 +3,11 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Communication; using TweetLib.Communication;
namespace TweetDuck.Core.Other.Management{ namespace TweetDuck.Core.Management{
sealed class VideoPlayer : IDisposable{ sealed class VideoPlayer : IDisposable{
public bool Running{ public bool Running{
get{ get{
@@ -21,7 +22,7 @@ namespace TweetDuck.Core.Other.Management{
public event EventHandler ProcessExited; public event EventHandler ProcessExited;
private readonly Form owner; private readonly FormBrowser owner;
private string lastUrl; private string lastUrl;
private string lastUsername; private string lastUsername;
@@ -29,7 +30,7 @@ namespace TweetDuck.Core.Other.Management{
private DuplexPipe.Server currentPipe; private DuplexPipe.Server currentPipe;
private bool isClosing; private bool isClosing;
public VideoPlayer(Form owner){ public VideoPlayer(FormBrowser owner){
this.owner = owner; this.owner = owner;
this.owner.FormClosing += owner_FormClosing; this.owner.FormClosing += owner_FormClosing;
} }
@@ -56,10 +57,8 @@ namespace TweetDuck.Core.Other.Management{
currentProcess.EnableRaisingEvents = true; currentProcess.EnableRaisingEvents = true;
currentProcess.Exited += process_Exited; currentProcess.Exited += process_Exited;
#if DEBUG
currentProcess.BeginOutputReadLine(); currentProcess.BeginOutputReadLine();
currentProcess.OutputDataReceived += (sender, args) => Debug.WriteLine("VideoPlayer: "+args.Data); currentProcess.OutputDataReceived += process_OutputDataReceived;
#endif
} }
currentPipe.DisposeToken(); currentPipe.DisposeToken();
@@ -84,6 +83,7 @@ namespace TweetDuck.Core.Other.Management{
break; break;
case "download": case "download":
owner.AnalyticsFile.DownloadedVideos.Trigger();
TwitterUtils.DownloadVideo(lastUrl, lastUsername); TwitterUtils.DownloadVideo(lastUrl, lastUsername);
break; break;
@@ -146,6 +146,12 @@ namespace TweetDuck.Core.Other.Management{
} }
} }
private void process_OutputDataReceived(object sender, DataReceivedEventArgs e){
if (!string.IsNullOrEmpty(e.Data)){
Program.Reporter.Log("[VideoPlayer] "+e.Data);
}
}
private void process_Exited(object sender, EventArgs e){ private void process_Exited(object sender, EventArgs e){
int exitCode = currentProcess.ExitCode; int exitCode = currentProcess.ExitCode;

View File

@@ -24,19 +24,21 @@ namespace TweetDuck.Core.Notification.Example{
private readonly TweetNotification exampleNotification; private readonly TweetNotification exampleNotification;
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){ public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true); string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true).Replace("{avatar}", TweetNotification.AppLogo.Url);
#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 = TweetNotification.Example(exampleTweetHTML, 95); exampleNotification = TweetNotification.Example(exampleTweetHTML, 176);
} }
public override void HideNotification(){ public override void HideNotification(){
Location = ControlExtensions.InvisibleLocation; Location = ControlExtensions.InvisibleLocation;
} }
public override void FinishCurrentNotification(){}
public void ShowExampleNotification(bool reset){ public void ShowExampleNotification(bool reset){
if (reset){ if (reset){
LoadTweet(exampleNotification); LoadTweet(exampleNotification);

View File

@@ -12,7 +12,7 @@ using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
partial class FormNotificationBase : Form{ partial class FormNotificationBase : Form, AnalyticsFile.IProvider{
protected static int FontSizeLevel{ protected static int FontSizeLevel{
get{ get{
switch(TweetDeckBridge.FontSize){ switch(TweetDeckBridge.FontSize){
@@ -90,6 +90,8 @@ namespace TweetDuck.Core.Notification{
} }
} }
public AnalyticsFile AnalyticsFile => owner.AnalyticsFile;
protected override bool ShowWithoutActivation => true; protected override bool ShowWithoutActivation => true;
protected float DpiScale { get; } protected float DpiScale { get; }
@@ -139,8 +141,8 @@ namespace TweetDuck.Core.Notification{
DpiScale = this.GetDPIScale(); DpiScale = this.GetDPIScale();
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory; browser.SetupResourceHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); browser.SetupResourceHandler(TweetNotification.AppLogo);
Controls.Add(browser); Controls.Add(browser);
@@ -161,10 +163,6 @@ namespace TweetDuck.Core.Notification{
base.WndProc(ref m); base.WndProc(ref m);
} }
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
owner.TriggerAnalyticsEvent(e);
}
// event handlers // event handlers
private void owner_FormClosed(object sender, FormClosedEventArgs e){ private void owner_FormClosed(object sender, FormClosedEventArgs e){

View File

@@ -5,7 +5,6 @@ using System.Windows.Forms;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Plugins; using TweetDuck.Plugins;
@@ -128,7 +127,7 @@ namespace TweetDuck.Core.Notification{
} }
blockXButtonUp = true; blockXButtonUp = true;
this.InvokeAsyncSafe(() => TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationExtraMouseButton)); this.InvokeAsyncSafe(AnalyticsFile.NotificationExtraMouseButtons.Trigger);
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){ else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
@@ -94,7 +93,7 @@ namespace TweetDuck.Core.Notification{
} }
needsTrim |= tweetQueue.Count >= TrimMinimum; needsTrim |= tweetQueue.Count >= TrimMinimum;
TriggerAnalyticsEvent(AnalyticsFile.Event.DesktopNotification); AnalyticsFile.DesktopNotifications.Trigger();
} }
public override void HideNotification(){ public override void HideNotification(){

View File

@@ -1,32 +1,50 @@
using System; using System.Drawing;
using TweetLib.Audio; using System.IO;
using System.Windows.Forms;
using CefSharp;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Settings;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class SoundNotification : IDisposable{ static class SoundNotification{
public string SupportedFormats => player.SupportedFormats; public const string SupportedFormats = "*.wav;*.ogg;*.mp3;*.flac;*.opus;*.weba;*.webm";
public event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly AudioPlayer player; public static IResourceHandler CreateFileHandler(string path){
string mimeType;
public SoundNotification(){ switch(Path.GetExtension(path)){
this.player = AudioPlayer.New(); case ".weba":
this.player.PlaybackError += Player_PlaybackError; case ".webm": mimeType = "audio/webm"; break;
case ".wav": mimeType = "audio/wav"; break;
case ".ogg": mimeType = "audio/ogg"; break;
case ".mp3": mimeType = "audio/mp3"; break;
case ".flac": mimeType = "audio/flac"; break;
case ".opus": mimeType = "audio/ogg; codecs=opus"; break;
default: mimeType = null; break;
} }
public void Play(string file){ try{
player.Play(file); return ResourceHandler.FromFilePath(path, mimeType);
} }catch{
FormBrowser browser = FormManager.TryFind<FormBrowser>();
public bool SetVolume(int volume){ browser?.InvokeAsyncSafe(() => {
return player.SetVolume(volume); 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);
private void Player_PlaybackError(object sender, PlaybackErrorEventArgs e){ Button btnViewOptions = form.AddButton("View Options");
PlaybackError?.Invoke(this, e); btnViewOptions.Width += 16;
} btnViewOptions.Location = new Point(btnViewOptions.Location.X-16, btnViewOptions.Location.Y);
public void Dispose(){ if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnViewOptions){
player.Dispose(); browser.OpenSettings(typeof(TabSettingsSounds));
}
}
});
return null;
}
} }
} }
} }

View File

@@ -1,12 +1,15 @@
using System; using System;
using System.Text; using System.Text;
using CefSharp;
using TweetDuck.Core.Bridge; using TweetDuck.Core.Bridge;
using TweetDuck.Data;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class TweetNotification{ sealed class TweetNotification{
private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>"; private const string DefaultHeadLayout = @"<html id='tduck' class='os-windows txt-size--14' data-td-font='medium' data-td-theme='dark'><head><meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'><style type='text/css'>body{background:#222426}</style>";
private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css"); private static readonly string CustomCSS = ScriptLoader.LoadResource("styles/notification.css");
public static readonly ResourceLink AppLogo = new ResourceLink("https://ton.twimg.com/tduck/avatar", ResourceHandler.FromByteArray(Properties.Resources.avatar, "image/png"));
public static TweetNotification Example(string html, int characters){ public static TweetNotification Example(string html, int characters){
return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true); return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true);

View File

@@ -1,26 +1,27 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using TweetDuck.Data.Serialization; using TweetDuck.Data.Serialization;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Local")]
sealed class AnalyticsFile{ sealed class AnalyticsFile{
private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>{ private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>();
HandleUnknownProperties = FileSerializer<AnalyticsFile>.IgnoreProperties("CountGCReloads")
};
static AnalyticsFile(){ static AnalyticsFile(){
Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{ Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{
ConvertToString = value => value.ToBinary().ToString(), ConvertToString = value => value.ToBinary().ToString(),
ConvertToObject = value => DateTime.FromBinary(long.Parse(value)) ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
}); });
Serializer.RegisterTypeConverter(typeof(Counter), new SingleTypeConverter<Counter>{
ConvertToString = value => value.Value.ToString(),
ConvertToObject = value => int.Parse(value)
});
} }
public enum Event{ public static readonly AnalyticsFile Dummy = new AnalyticsFile(null);
OpenOptions, OpenPlugins, OpenAbout, OpenGuide,
DesktopNotification, SoundNotification, MuteNotification,
BrowserContextMenu, BrowserExtraMouseButton,
NotificationContextMenu, NotificationExtraMouseButton, NotificationKeyboardShortcut,
TweetScreenshot, TweetDetail, VideoPlay
}
// STATE PROPERTIES // STATE PROPERTIES
@@ -30,57 +31,57 @@ namespace TweetDuck.Core.Other.Analytics{
// USAGE DATA // USAGE DATA
public int CountOpenOptions { get; private set; } = 0; public Counter OpenOptions { get; private set; } = 0;
public int CountOpenPlugins { get; private set; } = 0; public Counter OpenPlugins { get; private set; } = 0;
public int CountOpenAbout { get; private set; } = 0; public Counter OpenAbout { get; private set; } = 0;
public int CountOpenGuide { get; private set; } = 0; public Counter OpenGuide { get; private set; } = 0;
public int CountDesktopNotifications { get; private set; } = 0; public Counter DesktopNotifications { get; private set; } = 0;
public int CountSoundNotifications { get; private set; } = 0; public Counter SoundNotifications { get; private set; } = 0;
public int CountMuteNotifications { get; private set; } = 0; public Counter NotificationMutes { get; private set; } = 0;
public int CountBrowserContextMenus { get; private set; } = 0; public Counter BrowserContextMenus { get; private set; } = 0;
public int CountBrowserExtraMouseButtons { get; private set; } = 0; public Counter BrowserExtraMouseButtons { get; private set; } = 0;
public int CountNotificationContextMenus { get; private set; } = 0; public Counter NotificationContextMenus { get; private set; } = 0;
public int CountNotificationExtraMouseButtons { get; private set; } = 0; public Counter NotificationExtraMouseButtons { get; private set; } = 0;
public int CountNotificationKeyboardShortcuts { get; private set; } = 0; public Counter NotificationKeyboardShortcuts { get; private set; } = 0;
public int CountTweetScreenshots { get; private set; } = 0; public Counter BrowserReloads { get; private set; } = 0;
public int CountTweetDetails { get; private set; } = 0; public Counter CopiedUsernames { get; private set; } = 0;
public int CountVideoPlays { get; private set; } = 0; public Counter ViewedImages { get; private set; } = 0;
public Counter DownloadedImages { get; private set; } = 0;
public Counter DownloadedVideos { get; private set; } = 0;
public Counter UsedROT13 { get; private set; } = 0;
public Counter TweetScreenshots { get; private set; } = 0;
public Counter TweetDetails { get; private set; } = 0;
public Counter VideoPlays { get; private set; } = 0;
// END OF DATA // END OF DATA
public event EventHandler PropertyChanged;
private readonly string file; private readonly string file;
private AnalyticsFile(string file){ private AnalyticsFile(string file){
this.file = file; this.file = file;
} }
public void TriggerEvent(Event e){ private void SetupProperties(){
switch(e){ foreach(Counter counter in GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(prop => prop.PropertyType == typeof(Counter)).Select(prop => (Counter)prop.GetValue(this))){
case Event.OpenOptions: ++CountOpenOptions; break; counter.SetOwner(this);
case Event.OpenPlugins: ++CountOpenPlugins; break;
case Event.OpenAbout: ++CountOpenAbout; break;
case Event.OpenGuide: ++CountOpenGuide; break;
case Event.DesktopNotification: ++CountDesktopNotifications; break;
case Event.SoundNotification: ++CountSoundNotifications; break;
case Event.MuteNotification: ++CountMuteNotifications; break;
case Event.BrowserContextMenu: ++CountBrowserContextMenus; break;
case Event.BrowserExtraMouseButton: ++CountBrowserExtraMouseButtons; break;
case Event.NotificationContextMenu: ++CountNotificationContextMenus; break;
case Event.NotificationExtraMouseButton: ++CountNotificationExtraMouseButtons; break;
case Event.NotificationKeyboardShortcut: ++CountNotificationKeyboardShortcuts; break;
case Event.TweetScreenshot: ++CountTweetScreenshots; break;
case Event.TweetDetail: ++CountTweetDetails; break;
case Event.VideoPlay: ++CountVideoPlays; break;
} }
} }
public void OnPropertyChanged(){
PropertyChanged?.Invoke(this, EventArgs.Empty);
}
public void Save(){ public void Save(){
if (file == null){
return;
}
try{ try{
Serializer.Write(file, this); Serializer.Write(file, this);
}catch(Exception e){ }catch(Exception e){
@@ -97,7 +98,34 @@ namespace TweetDuck.Core.Other.Analytics{
Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e); Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
} }
config.SetupProperties();
return config; return config;
} }
public interface IProvider{
AnalyticsFile AnalyticsFile { get; }
}
public sealed class Counter{
public int Value { get; private set; }
private AnalyticsFile owner;
public Counter(int value){
this.Value = value;
}
public void SetOwner(AnalyticsFile owner){
this.owner = owner;
}
public void Trigger(){
++Value;
owner?.OnPropertyChanged();
}
public static implicit operator int(Counter counter) => counter.Value;
public static implicit operator Counter(int value) => new Counter(value);
}
} }
} }

View File

@@ -1,4 +1,8 @@
using System; // Uncomment to debug reports locally
// #define ANALYTICS_LOCALHOST
// #define ANALYTICS_INSTANT
using System;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
@@ -8,8 +12,15 @@ using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
sealed class AnalyticsManager : IDisposable{ sealed class AnalyticsManager : IDisposable{
private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(7); private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(14);
private static readonly Uri CollectionUrl = new Uri("https://tweetduck.chylex.com/breadcrumb/report");
private static readonly Uri CollectionUrl = new Uri(
#if (DEBUG && ANALYTICS_LOCALHOST)
"http://localhost/newhome/tweetduck/~breadcrumb/request.php?type=report"
#else
"https://tweetduck.chylex.com/breadcrumb/report"
#endif
);
public AnalyticsFile File { get; } public AnalyticsFile File { get; }
@@ -20,7 +31,9 @@ namespace TweetDuck.Core.Other.Analytics{
public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){ public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){
this.browser = browser; this.browser = browser;
this.plugins = plugins; this.plugins = plugins;
this.File = AnalyticsFile.Load(file); this.File = AnalyticsFile.Load(file);
this.File.PropertyChanged += File_PropertyChanged;
this.currentTimer = new Timer{ SynchronizingObject = browser }; this.currentTimer = new Timer{ SynchronizingObject = browser };
this.currentTimer.Elapsed += currentTimer_Elapsed; this.currentTimer.Elapsed += currentTimer_Elapsed;
@@ -34,9 +47,15 @@ namespace TweetDuck.Core.Other.Analytics{
else{ else{
RestartTimer(); RestartTimer();
} }
#if (DEBUG && ANALYTICS_INSTANT)
SendReport();
#endif
} }
public void Dispose(){ public void Dispose(){
File.PropertyChanged -= File_PropertyChanged;
if (saveTimer.Enabled){ if (saveTimer.Enabled){
File.Save(); File.Save();
} }
@@ -45,8 +64,7 @@ namespace TweetDuck.Core.Other.Analytics{
saveTimer.Dispose(); saveTimer.Dispose();
} }
public void TriggerEvent(AnalyticsFile.Event e){ private void File_PropertyChanged(object sender, EventArgs e){
File.TriggerEvent(e);
saveTimer.Enabled = true; saveTimer.Enabled = true;
} }
@@ -95,7 +113,7 @@ namespace TweetDuck.Core.Other.Analytics{
Task.Factory.StartNew(() => { Task.Factory.StartNew(() => {
AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins); AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins);
#if DEBUG #if (DEBUG && !ANALYTICS_INSTANT)
System.Diagnostics.Debugger.Break(); System.Diagnostics.Debugger.Break();
#endif #endif
@@ -122,6 +140,14 @@ namespace TweetDuck.Core.Other.Analytics{
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;
} }
#if DEBUG
System.IO.Stream responseStream = e.Response.GetResponseStream();
if (responseStream != null){
System.Diagnostics.Debug.WriteLine(new System.IO.StreamReader(responseStream).ReadToEnd());
}
#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

@@ -12,6 +12,7 @@ using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
namespace TweetDuck.Core.Other.Analytics{ namespace TweetDuck.Core.Other.Analytics{
static class AnalyticsReportGenerator{ static class AnalyticsReportGenerator{
@@ -37,15 +38,24 @@ namespace TweetDuck.Core.Other.Analytics{
{ "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" }, { "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
0, 0,
{ "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) }, { "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
{ "Clear Cache Automatically" , Bool(SysConfig.ClearCacheAutomatically) },
{ "Clear Cache Threshold" , Exact(SysConfig.ClearCacheThreshold) },
0, 0,
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) }, { "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
{ "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) }, { "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) }, { "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) }, { "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) }, { "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) }, { "Animated Images" , Bool(UserConfig.EnableAnimatedImages) },
0,
{ "Smooth Scrolling" , Bool(UserConfig.EnableSmoothScrolling) },
{ "Custom Browser" , CustomBrowser },
{ "Zoom" , Exact(UserConfig.ZoomLevel) }, { "Zoom" , Exact(UserConfig.ZoomLevel) },
0, 0,
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) },
{ "Spell Check Language" , UserConfig.SpellCheckLanguage.ToLower() },
{ "Translation Target Language" , UserConfig.TranslationTarget },
0,
{ "Updates" , Bool(UserConfig.EnableUpdateCheck) }, { "Updates" , Bool(UserConfig.EnableUpdateCheck) },
{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) }, { "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
0, 0,
@@ -70,8 +80,8 @@ namespace TweetDuck.Core.Other.Analytics{
{ "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) }, { "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) }, { "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
0, 0,
{ "Plugins All" , List(plugins.Plugins.Select(plugin => plugin.Identifier)) }, { "Plugins All" , List(plugins.Plugins.Select(Plugin)) },
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) }, { "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(Plugin)) },
0, 0,
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") }, { "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") }, { "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
@@ -87,21 +97,27 @@ namespace TweetDuck.Core.Other.Analytics{
{ "Reply Account Mode" , ReplyAccountConfigFromPlugin }, { "Reply Account Mode" , ReplyAccountConfigFromPlugin },
{ "Template Count" , Exact(TemplateCountFromPlugin) }, { "Template Count" , Exact(TemplateCountFromPlugin) },
0, 0,
{ "Opened Options" , LogRound(file.CountOpenOptions, 4) }, { "Opened Options" , LogRound(file.OpenOptions, 4) },
{ "Opened Plugins" , LogRound(file.CountOpenPlugins, 4) }, { "Opened Plugins" , LogRound(file.OpenPlugins, 4) },
{ "Opened About" , LogRound(file.CountOpenAbout, 4) }, { "Opened About" , LogRound(file.OpenAbout, 4) },
{ "Opened Guide" , LogRound(file.CountOpenGuide, 4) }, { "Opened Guide" , LogRound(file.OpenGuide, 4) },
{ "Desktop Notifications" , LogRound(file.CountDesktopNotifications, 5) }, { "Desktop Notifications" , LogRound(file.DesktopNotifications, 5) },
{ "Sound Notifications" , LogRound(file.CountSoundNotifications, 5) }, { "Sound Notifications" , LogRound(file.SoundNotifications, 5) },
{ "Mute Notifications" , LogRound(file.CountMuteNotifications, 2) }, { "Notification Mutes" , LogRound(file.NotificationMutes, 2) },
{ "Browser Context Menus" , LogRound(file.CountBrowserContextMenus, 2) }, { "Browser Context Menus" , LogRound(file.BrowserContextMenus, 2) },
{ "Browser Extra Mouse Buttons" , LogRound(file.CountBrowserExtraMouseButtons, 2) }, { "Browser Extra Mouse Buttons" , LogRound(file.BrowserExtraMouseButtons, 2) },
{ "Notification Context Menus" , LogRound(file.CountNotificationContextMenus, 2) }, { "Notification Context Menus" , LogRound(file.NotificationContextMenus, 2) },
{ "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) }, { "Notification Extra Mouse Buttons" , LogRound(file.NotificationExtraMouseButtons, 2) },
{ "Notification Keyboard Shortcuts" , LogRound(file.CountNotificationKeyboardShortcuts, 2) }, { "Notification Keyboard Shortcuts" , LogRound(file.NotificationKeyboardShortcuts, 2) },
{ "Tweet Screenshots" , LogRound(file.CountTweetScreenshots, 2) }, { "Browser Reloads" , LogRound(file.BrowserReloads, 2) },
{ "Tweet Details" , LogRound(file.CountTweetDetails, 2) }, { "Copied Usernames" , LogRound(file.CopiedUsernames, 2) },
{ "Video Plays" , LogRound(file.CountVideoPlays, 4) } { "Viewed Images" , LogRound(file.ViewedImages, 2) },
{ "Downloaded Images" , LogRound(file.DownloadedImages, 2) },
{ "Downloaded Videos" , LogRound(file.DownloadedVideos, 2) },
{ "Used ROT13" , LogRound(file.UsedROT13, 2) },
{ "Tweet Screenshots" , LogRound(file.TweetScreenshots, 2) },
{ "Tweet Details" , LogRound(file.TweetDetails, 2) },
{ "Video Plays" , LogRound(file.VideoPlays, 4) }
}.FinalizeReport(); }.FinalizeReport();
} }
@@ -112,6 +128,7 @@ namespace TweetDuck.Core.Other.Analytics{
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 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)"));
@@ -182,6 +199,12 @@ namespace TweetDuck.Core.Other.Analytics{
ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray(); ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
} }
private static string CustomBrowser{
get{
return Path.GetFileName(UserConfig.BrowserPath) ?? string.Empty;
}
}
private static string TrayMode{ private static string TrayMode{
get{ get{
switch(UserConfig.TrayBehavior){ switch(UserConfig.TrayBehavior){

View File

@@ -38,7 +38,6 @@ namespace TweetDuck.Core.Other {
// //
this.pictureLogo.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; this.pictureLogo.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.pictureLogo.ErrorImage = null; this.pictureLogo.ErrorImage = null;
this.pictureLogo.Image = ((System.Drawing.Image)(resources.GetObject("pictureLogo.Image")));
this.pictureLogo.InitialImage = null; this.pictureLogo.InitialImage = null;
this.pictureLogo.Location = new System.Drawing.Point(12, 12); this.pictureLogo.Location = new System.Drawing.Point(12, 12);
this.pictureLogo.Name = "pictureLogo"; this.pictureLogo.Name = "pictureLogo";

View File

@@ -1,4 +1,6 @@
using System.ComponentModel; using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -17,6 +19,10 @@ namespace TweetDuck.Core.Other{
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website)); labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink)); labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));
labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink)); labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink));
MemoryStream logoStream = new MemoryStream(Properties.Resources.avatar);
pictureLogo.Image = Image.FromStream(logoStream);
Disposed += (sender, args) => logoStream.Dispose();
} }
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){

View File

@@ -1,246 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureLogo.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
bGUAAEjHnZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZE
sRAUVLAHJAgoMRhFVCxvRtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs
AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnpfIHomAARm7M5GSwRF4g4
JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKjmNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR
3xKxRoowlSviN+LYVA4zAwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd
li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0NDQzMv2qUP91829K3NtF
ehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGX
wzISF/QP/U+Hv6GvvmckPu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF
hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygKESDR+8Vd/6NvvvgwIH55
4SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TPEqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJ
VgMWSASpgA+yQB7YBApBMdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB
5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeuQIPQXWgMmoZ+h97BCEyC
qbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+FO+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiE
j6xHipAKpAFpRbqRPuQmMorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I
1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+TBvmHGYQM46Zw2Kx8lh9
rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgoHA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhG
fDf+On4Cv0CQJmgT7AghhCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp
B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkjiuSReUlPSSXK1ZK5kheQJ
yeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJC
YVE2UxopFykTVAxVm+pFTaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln
yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf4aLCzFLqUtulrKVFS48v
vacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0Cvp
vfRZVUVVT1Whar3qgOqCmrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L
Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6iXo3edX1Y31Kfq79Pf9AA
bWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtrGEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0z
llmN2S1zsrm7+QbzLvMXy/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW
ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7A/ajDqoOTIcGh8eO6o5s
xybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4erkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6
eO7yHPFS8mJ5NXvNelt5r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw
YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX8LLw0QjjiHUR1yIVIrmR
XVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUKq1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNm
WS6svaxnbEd2OXuaY8cp40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl
xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BHuEU4lmmfWZP5Jiss60S2
dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNaV78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8
V5vDN3cXKBVsLBjf4rGlpVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za
Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJe4V7Ryt9K7uqNKp2Vr2v
Tqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5wD9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqb
PhziHRo9HHS4t9mqufmI4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/
0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69AzhTMGZT2dzz86dSz83cz7h
/HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9tdPnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavr
XTesb3QPLh88M+QwdP6m681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS
fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnafvvF05dOJZ+nPFmYKf5X+
tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aueuYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/
6H7o/ujz8cGn1E+f/gUDmPP8usTo0wAAAAlwSFlzAAAuIgAALiIBquLdkgAAEVpJREFUeF7tXQl0VNd5
Thq3Tk7tc+qmiZMm7YmdJj1OT3Gd0kTMG3mJnZQEJzaSjXETx0lqNwt2jWscQGixBAJLSMhgA8YYbIyQ
hHYJI4lFGMwioQWxyewgMdoXtKFd4u/337lPjDRv9pk3g+A75zsMWv577/fd+9/l3Rl9johu0Y/U/OIt
6sfrLwIYT2de+SJ4F/j34LfB+8Ap4L9J8uvvgfy9b4DfBO8EPy9DBBwC3gCI93fgDHAuuA7cDVaDzWAX
OAAOgcNgP9gN8vc+A4vB9eBr4M/Be8GAMiPgDIBAXwVDwbXgEbAPJC+RDToGvgfOBr8mi/UbAsIACMFp
ZRaYC7aBWuL5gh3gdvBZ8C5ZHV3hVwPQaM7hS0ETqCWQnqwDE8Apsnq6wC8GoJEPgFtAzt9aYviTPJ9k
gkGyuj6FrgagUd8CeVIcBLUaH0gcATeD35XV9wl0MQCN4OXjn8EroFZjA5m8qooE/1o2x6vwuQGouAEs
A7UadyPxOPgj2SyvwacGoMILwRsh3ThLTksx4BdkEz2GTwxABb8C8pJSqxGTgTvAb8jmegSvG4CK/QvI
O1Wtik8mXgSnyma7Da8agAo9Auq5kfI3+Shkhmy+W/CaAajI42CvrNjNRJ7jZksZXIZXDEAFWPxA3FTp
xVHQLRM8NgAFPwTejD1/InkH/biUxWl4ZAAK5LP3mynnO+JV0KWJ2W0DUNCXwVOy4Fu8zhrQ6SWqJwbk
WxQa2Mxop6fSWyk0rYlCUhspZEs9zdxSJ8iv+WuhW5vpqYw27d93nXvB26RUduGWAQg+36KwwCULv7VF
iDx3Wx1tPNxCBy92UU17P7VdHaLWniE619JLu8900Nv7m+jFLBgCM9gs/l3NmCqlqU+nt+D/msYtk3LZ
hcsGIPB/gPaPF7jyKrW+rwNZnJCUBoraUU9H63po9No1NM8++odGac/ZDno5r45C2QitESGF59gv5dVT
clUHzc5gEyb8XOaVa6DDsyMU67wBCHg7WCULsEkezi9/jEptbfKLCdzrn0s30b5znWiS6xgeuUaby1so
NKVubDSwGZzCOG3NzTdRQXW7+NnM4+b2atUDPAPeIeXTBEK4ZMA8i+Ca5Ir+LrOOOvtHKGkfcit6kp4m
sBh/zDGRqaMfzfEMh5CuZiXXUOgWE72Ua6L1Jc10qqmXRkbNo4nT2PMw2sHcESvl0wTCOGcAAv0DyFtv
rULGyAKsO9SMUERD6EnRO+t1M4F76++zTNTUNSjK9wYutvVhzujTTGGrMG/wqNCqiwX5IsA/SxmtgDBO
G7DBIqhN8oR3suEqQpnRh7watcOcU31pAvfCWUgZ1Y3Xy/YlCk+Z28rlqrTTvkwpoxUQyrEBCPCvoONz
fVTg1xkm6hkYQajr4Mktdpd5uecrE9jg5IpWWaJvwRN1SHKtaA/PCfMLmyiq2DxPaNUN5KMKg5RzHBDO
KQNSLYLZJFfgVSz3tMBDeM0BrMNlr9H6fXfJ8V7AErK7f1iW5hsMjYxSSnkjPbnxLP1vbq2YE04399KV
vmGak9/kqF0FUs5xQFj7BuAXvwM6ddDGOfiNnQ0IYxvbTrYjVWDiwkrFW6OB83DaEd/3/obOfiq71Ekt
3YOkzgg8NUTxPOdgLpiV1TE6K7vzASnrGBDCoQF8b0cz6ESyqDG77BvAOI2VxJycy7bX2q4QJoYgDVxo
7ZPR9QVv4OzNb7OyOll8mpnWTNPXnlwnZR0DQtg2AAHuAPnCkmbwieQREFZQjzCOwfPChlJs/3mt7cFo
YAPn5NTRsFwa6on3kII05zUIjt4uXv9icx099u5pCn7rKBkSynoMC1K/LuUVQBi7BswcF9gBWYzfZ5vE
RsZZnG/powjsVsXc4IYR/DtLix2POm+C9wGrPjWfK1nWl3v6MxA+FB1xxgeX6OF3TpKSdASsIiMMMK48
TkpM4RwprwDC2TUgTw3uFFEZXgo2dA4glPNgu8pruyms0HxAJnaWThrBP7v2oHnfoQd48xWBelqmHe7t
Is2kNNJ/rj9HD648RsoKC+FVwgBj/MGDUl4BhNQ2AIH/FuTLq1aNtkfuFYWfXUEo18FGHKvrofg9DfRs
mknEcjQq2ABejeiFyEKTWXyUbU4z7fSLj0z06NpTELnKLLyl6BOZVDVgDM+8R8ps14AQy4Y6S1fmAXvg
npZ/oo0iiuro2VSYkdIgVhocX0zc0hQ2YDWWt3phIdrGp5+hW1vpZxsv0SOcZrR6uy1iFAQvLvqDlNmu
Ae9aCus0IQzn8/LL3QjnHbAZpZe6RE9fsL2O/jvTJFIdlzMz2URRhZflT/oer+fX0E/ePUXBttKMI3Ia
enNfhpRZ2wAI+XnQ4amnLXLamPdxHQ1i4+IL8AqqHvMMp6vdZ65Q8en2sXW5r/HbjZVYzVRoi+sMYZwx
saxWWbj1Swhn04B/BPkQSVNgu+TUAHKefB/LzMkE3s3PXgfxsbLRFNd5XlMWZT6AkDYN+JmVsE5QLEPz
WujXGY0iZ3N60GOHqheuDgzT4++U80SqJarz5DQUXfAcQlobACE5/fAb4jRFtkdOPatLWqmrf4RSKlvo
1fzL9MSGM7TuQJ04R7nR0dDRSw8mlGqL6gpXniDjkp2xyoKULyCslQH81MutCZgNWLz7+qaI83Jz9yAm
0A5q6/HeGb2/cPxyBxnivWEAT8T7c4Mjc+5EWCsD/gbkt4JqimyP6rGA+sRosqHgWD0Zlh/WFtUVsgHL
S44aI3O/hrBWBvCboN273cwTMJaHvEKZjEgsOkNKogcrIJViJVTeaIzefi/CWhnA7zJvGiesC+TVT85x
8wPryYYXNpl3upqiukQYsKKyR1my63sIa2UAv3WU3xelKbAj8k71lXz/nE76Em09A/Tjt5B+PF0BqUw6
MogN2RSEtjKAHz+6twdg8k6Yz4NOuXceFKgorm7yTv5XmXRkVIk78H2EtjLgfpBv+moL7AR5Mv5NRp1X
byf4GzH5n3kn/6vkkbS8dCpCWxnAnzrikQFMPiRbWFAnbkXc6OAN2BNreAPmjfwvyQbEH9I0gD/yxfM3
W/BxRFqTuBrYOzj+lsSNht0nG72bfphJVddgwL8jvJUBHk3C4yhNeCXfRJfaPL+p5i+8ln4Cq59KbSHd
ZVLloLJs7/0Ib2UAL0NbxgnpCWEC75Bnp5rog7IW6ujz7dURb+NsYxdvmswpQ0tItyiWod1KTJHmMpQ3
YvxhR9qCukiekGenN4tHjU9+VENPbzpLcbtq6GS9954X+BLLtp/y7uTLNG/EmozRBd9GEVYG8Gf37LEU
0RNy7/+o8gqdae6jQxc76eCFTqqo7aTGzsBPSRdbeuihRG/3fpANiC85qkTkaB5F8Adr8CeaaArqKsUj
yiJ9by14C2/keXnpqXIV5pTYPblB8zZ9BcVYGcDH0f9nKaJH5I1ZSgMduNCF8DcOjtS0k+L13C/JBize
EWeYn3wbihpvgHwgw+/71RbUDfIo+FNuvdWl3UAFn+a+KM59vLzyUWl+IPMbFHVd97EXZgPuAb3yKSfq
ZSUeBbFFNSgi8PHRwRrzut8Xvd/Ma8FR+XYfSf4FyJ+PoymqM1TvRIakNtP098/TQ5h4psWXUnzBaRQT
uDhZ1yknXi/uei0JHZTlpbVBr6z7IoqzNkAlRHR9Ira4E/lEcv31O5Hq9Q2Qe9bCrJPU3T+EYgILPajT
r96v8F3qYSL/G5bszuKOjiLHeP2FBETkj5G0FlmDapp5amsrzfiwhh5ZPeFOpCXxNSWxnGauKaed2OI7
8eZF3RCejXqjbj5MPWYDInJfZo1RpF0D+EOX7L4nbOzqdWqTSDN8J3Kst2sVbkF+sMGj4fmNlZR2uFas
uYflg3t/mLJy11lf530Q6/+3qoaw+vkOa4xibRvAgMj8oaYTRLe+eu3UnUgt8mhgIxLKKBhLvsdXltDc
zRV0qUXfXfKHBy7pID4ongUfKpfyOmXAf00U3ubVa0+IOCzAM+vKqOxCG6qhHzbziodvOvhq0rUk0k9Q
WPpLUl4BVMG2AbOyu+4CW1j8Jx1dvXaHLDx6/09XHabU0loaHNb32cG7n1zQT3w+fkgs75r6Pwl3S3kF
UA3bBjB+vvH8CnH1WqYLzeCukEcNVhnc459YXSaGf0evvk/O2OjovGqZdnQQn/l2NQVF5G2Qso4B1bFv
gCEs/X6kmhHNoCp5NHBDBPm1ShYbTKwQPZ1723T09tfTT4hnrL2D+h9N17Zdpd9uNI88UUet9viCGAFB
r29WpKxjQJXsG8AwLtv3sbhOpxUYjZi+5hg9s+E4zVpfRT9ZeZh+nFRKP11ZRqFry+mFD6soKreakg/V
iPzuz/V/TqVJ1E2s83UVX9yE2294baPV3y5AtRwboIRnT0OgUavAko++fZR++cFxKj7VQgPDI9Q3OCKE
HhgKjLOfc03d9ErqcX1TjiVhADLJdCnnOKB6jg1gGOP2pwsntQpAbxLzw/JSCsuqprNocCCgoaOPEgrP
0MOJJeZjZT17vUo++Vz6yU4poxVQTScNWJT5XfSefvNmQqMgJhuBhirxJTQ/4wRVXGqnUT9c0Drf3E3L
IfyPON2Ina0fer0gVoxJVcNBr31g9QZtFaiucwYwjEt2xdqcCywpRgRWOph0f7m+QuT/y+1XfbrD5Ztr
24/V05wtR8Wmzr/CS7JWscWrpHyaQNWdN0AJS7vDmFh2zmYqmkiZmnjF8WBCibhbuWbPeTqMyZiXnp4Y
wgdnfF2cl7Gc3x9Db+ccr/sEa4u87l9R3hAcnvllKZ8m0BTnDWAo4VmPGldyIXZSkRalGdwzeWTwaonP
gRZlV9O6vRcoq8JEu7E0LTnXSidMHSKNHK29Qofwf76Xw99/Dz8XiRXV7z48QjPeLkOqg+gwN2BEtyRv
WCNyQqRsNuGyAQxlya44p1KRPaqGQDxhCnrvGFlYlRZf558TcwxP+IEmuCXFI8eda6RcduGWAcaFaX9p
jD/0qdOp6GYia7K8pCp4UfqXpFx24ZYBDGVB6jeNKypMt0ywoPm8p9UYkW3zI8omwm0DGIbXN//AuKKy
VxSsVaGbiWYNhrBpdenPnHhkAAMTzUwUPnxTm6C2PWrbc1IWp+GxAQxDePavxlXkpiLazO2OKfqjlMMl
eMUABruPCt1kI4HbiuVm9PY/SRlchtcMYBjCMkJQMcwJN8HEzB0tqWrIGF3wvGy+W/CqAQwlLH2aknDY
xGthzYpPBnIHW1HRYozMeUw222143QDGtD8n3yP2CcKESZaSeAOaUFpuDM+8TzbXI/jEAIZxYepfGaIL
48yVngQpiVMO2qHEFq81Lkr32p819JkBKgyLMmeYD/Bu4NHAdU8sr1Ui85+SzfIafG4Aw7go4y5jbPEy
8TzhRpobxMitGjYu3fOOMWzrV2VzvApdDFDxw3mbpyhL92aL09RATkuibhitb35agIn2h7L6PoGuBqgw
hOc8Zow7UCQayyMiIPYOnONlmow7sEeJzPPoL+Q5C78YoAI76KBpMTtXK4nlLcZV6HVqz9MUyEfkMpkr
KnuMSz/ZpETkPCyrpwv8aoCKH8zdcPe0yG0vYum6X8wTLIivRoZczQgmVQ2izBIluvBVZeHWb8nq6IqA
MMASWDX9k+GN7X8wxO5JU5aXnjeLxoKxIVI4YYzKCQKrX7cUWpiJf/n7CWW1yrJ9WcaYwpeU8Cynj419
hYAzwBJB8zbdHhyVd78xpuA5ZfHOWEzgWeixFVgSNiBldGG0DICj4uE7PyEzvx4Q6YQ/ECm+5Cgm0jxl
8Y5lyhsfPw9zpyrzU3zyp8ndRUAbwFDrZViQdlvQ/NQ7kKPvNkYX3GuIKbpPWfrJFP7IFyXu4FQYM1V5
c//3lWV7p/A70JWobfcq4dlfN4Zn3WmYn3y7GseSgYCxulhW7Bb1p+YXb1Ev0uf+H9A3E1Z4VJUaAAAA
AElFTkSuQmCC
</value>
</data>
</root>

View File

@@ -6,7 +6,6 @@ using CefSharp.WinForms;
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.Utils; using TweetDuck.Core.Utils;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using TweetDuck.Resources; using TweetDuck.Resources;
@@ -43,7 +42,7 @@ namespace TweetDuck.Core.Other{
FormBrowser owner = FormManager.TryFind<FormBrowser>(); FormBrowser owner = FormManager.TryFind<FormBrowser>();
if (owner != null){ if (owner != null){
owner.TriggerAnalyticsEvent(AnalyticsFile.Event.OpenGuide); owner.AnalyticsFile.OpenGuide.Trigger();
new FormGuide(url, owner).Show(owner); new FormGuide(url, owner).Show(owner);
} }
} }
@@ -55,7 +54,7 @@ namespace TweetDuck.Core.Other{
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
private FormGuide(string url, Form owner){ private FormGuide(string url, FormBrowser owner){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Guide"; Text = Program.BrandName+" Guide";
@@ -66,7 +65,7 @@ namespace TweetDuck.Core.Other{
} }
this.browser = new ChromiumWebBrowser(url){ this.browser = new ChromiumWebBrowser(url){
MenuHandler = new ContextMenuGuide(), MenuHandler = new ContextMenuGuide(owner),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser() RequestHandler = new RequestHandlerBrowser()

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 = buttonCount, TabIndex = 256-buttonCount,
Text = title, Text = title,
UseVisualStyleBackColor = true UseVisualStyleBackColor = true
}; };

View File

@@ -76,7 +76,7 @@ namespace TweetDuck.Core.Other{
} }
private void btnOpenFolder_Click(object sender, EventArgs e){ private void btnOpenFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", "\""+pluginManager.PathCustomPlugins+"\"")){} using(Process.Start("explorer.exe", '"'+pluginManager.PathCustomPlugins+'"')){}
} }
private void btnReload_Click(object sender, EventArgs e){ private void btnReload_Click(object sender, EventArgs e){

View File

@@ -33,7 +33,7 @@
// //
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.AutoSize = true; this.btnClose.AutoSize = true;
this.btnClose.Location = new System.Drawing.Point(449, 447); this.btnClose.Location = new System.Drawing.Point(449, 483);
this.btnClose.Name = "btnClose"; this.btnClose.Name = "btnClose";
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnClose.Size = new System.Drawing.Size(49, 23); this.btnClose.Size = new System.Drawing.Size(49, 23);
@@ -52,7 +52,7 @@
this.panelContents.Location = new System.Drawing.Point(135, 12); this.panelContents.Location = new System.Drawing.Point(135, 12);
this.panelContents.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3); this.panelContents.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3);
this.panelContents.Name = "panelContents"; this.panelContents.Name = "panelContents";
this.panelContents.Size = new System.Drawing.Size(363, 429); this.panelContents.Size = new System.Drawing.Size(363, 465);
this.panelContents.TabIndex = 1; this.panelContents.TabIndex = 1;
// //
// panelButtons // panelButtons
@@ -63,14 +63,14 @@
this.panelButtons.Location = new System.Drawing.Point(12, 12); this.panelButtons.Location = new System.Drawing.Point(12, 12);
this.panelButtons.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); this.panelButtons.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3);
this.panelButtons.Name = "panelButtons"; this.panelButtons.Name = "panelButtons";
this.panelButtons.Size = new System.Drawing.Size(124, 429); this.panelButtons.Size = new System.Drawing.Size(124, 465);
this.panelButtons.TabIndex = 0; this.panelButtons.TabIndex = 0;
// //
// btnManageOptions // btnManageOptions
// //
this.btnManageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.btnManageOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnManageOptions.AutoSize = true; this.btnManageOptions.AutoSize = true;
this.btnManageOptions.Location = new System.Drawing.Point(12, 447); this.btnManageOptions.Location = new System.Drawing.Point(12, 483);
this.btnManageOptions.Name = "btnManageOptions"; this.btnManageOptions.Name = "btnManageOptions";
this.btnManageOptions.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnManageOptions.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnManageOptions.Size = new System.Drawing.Size(101, 23); this.btnManageOptions.Size = new System.Drawing.Size(101, 23);
@@ -83,7 +83,7 @@
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(510, 482); this.ClientSize = new System.Drawing.Size(510, 518);
this.Controls.Add(this.btnManageOptions); this.Controls.Add(this.btnManageOptions);
this.Controls.Add(this.panelContents); this.Controls.Add(this.panelContents);
this.Controls.Add(this.panelButtons); this.Controls.Add(this.panelButtons);

View File

@@ -40,7 +40,7 @@ namespace TweetDuck.Core.Other{
AddButton("Locales", () => new TabSettingsLocales()); AddButton("Locales", () => new TabSettingsLocales());
AddButton("System Tray", () => new TabSettingsTray()); AddButton("System Tray", () => new TabSettingsTray());
AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins))); AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
AddButton("Sounds", () => new TabSettingsSounds()); AddButton("Sounds", () => new TabSettingsSounds(this.browser.PlaySoundNotification));
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)); AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS));
@@ -67,10 +67,13 @@ namespace TweetDuck.Core.Other{
FormClosing -= FormSettings_FormClosing; FormClosing -= FormSettings_FormClosing;
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
if (!dialog.IsRestarting){
browser.ResumeNotification(); browser.ResumeNotification();
BrowserProcessHandler.UpdatePrefs(); BrowserProcessHandler.UpdatePrefs();
ShouldReloadBrowser = dialog.ShouldReloadBrowser; ShouldReloadBrowser = dialog.ShouldReloadBrowser;
}
Close(); Close();
} }
else{ else{

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
@@ -9,12 +8,21 @@ namespace TweetDuck.Core.Other.Settings{
public IEnumerable<Control> InteractiveControls{ public IEnumerable<Control> InteractiveControls{
get{ get{
foreach(Panel panel in Controls.OfType<Panel>()){ IEnumerable<Control> FindInteractiveControls(Control parent){
foreach(Control control in panel.Controls){ foreach(Control control in parent.Controls){
if (control is Panel subPanel){
foreach(Control subControl in FindInteractiveControls(subPanel)){
yield return subControl;
}
}
else{
yield return control; yield return control;
} }
} }
} }
return FindInteractiveControls(this);
}
} }
protected BaseTabSettings(){ protected BaseTabSettings(){

View File

@@ -26,16 +26,17 @@
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.btnCancel = new System.Windows.Forms.Button(); this.btnCancel = new System.Windows.Forms.Button();
this.btnContinue = new System.Windows.Forms.Button(); this.btnContinue = new System.Windows.Forms.Button();
this.cbConfig = new System.Windows.Forms.CheckBox(); this.cbProgramConfig = new System.Windows.Forms.CheckBox();
this.cbSession = new System.Windows.Forms.CheckBox(); this.cbSession = new System.Windows.Forms.CheckBox();
this.cbPluginData = new System.Windows.Forms.CheckBox(); this.cbPluginData = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.panelExport = new System.Windows.Forms.Panel(); this.cbSystemConfig = new System.Windows.Forms.CheckBox();
this.panelDecision = new System.Windows.Forms.Panel(); this.panelSelection = new System.Windows.Forms.FlowLayoutPanel();
this.radioReset = new System.Windows.Forms.RadioButton(); this.panelDecision = new System.Windows.Forms.FlowLayoutPanel();
this.radioExport = new System.Windows.Forms.RadioButton();
this.radioImport = new System.Windows.Forms.RadioButton(); this.radioImport = new System.Windows.Forms.RadioButton();
this.panelExport.SuspendLayout(); this.radioExport = new System.Windows.Forms.RadioButton();
this.radioReset = new System.Windows.Forms.RadioButton();
this.panelSelection.SuspendLayout();
this.panelDecision.SuspendLayout(); this.panelDecision.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
@@ -56,95 +57,92 @@
this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnContinue.AutoSize = true; this.btnContinue.AutoSize = true;
this.btnContinue.Enabled = false; this.btnContinue.Enabled = false;
this.btnContinue.Location = new System.Drawing.Point(125, 97); this.btnContinue.Location = new System.Drawing.Point(119, 97);
this.btnContinue.Name = "btnContinue"; this.btnContinue.Name = "btnContinue";
this.btnContinue.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnContinue.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnContinue.Size = new System.Drawing.Size(45, 23); this.btnContinue.Size = new System.Drawing.Size(51, 23);
this.btnContinue.TabIndex = 3; this.btnContinue.TabIndex = 3;
this.btnContinue.Text = "Next"; this.btnContinue.Text = "Next";
this.btnContinue.UseVisualStyleBackColor = true; this.btnContinue.UseVisualStyleBackColor = true;
this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click); this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click);
// //
// cbConfig // cbProgramConfig
// //
this.cbConfig.AutoSize = true; this.cbProgramConfig.AutoSize = true;
this.cbConfig.Location = new System.Drawing.Point(0, 3); this.cbProgramConfig.Location = new System.Drawing.Point(3, 3);
this.cbConfig.Name = "cbConfig"; this.cbProgramConfig.Name = "cbProgramConfig";
this.cbConfig.Size = new System.Drawing.Size(104, 17); this.cbProgramConfig.Size = new System.Drawing.Size(104, 17);
this.cbConfig.TabIndex = 0; this.cbProgramConfig.TabIndex = 0;
this.cbConfig.Text = "Program Options"; this.cbProgramConfig.Text = "Program Options";
this.toolTip.SetToolTip(this.cbConfig, "Interface, notification, and update options."); this.toolTip.SetToolTip(this.cbProgramConfig, "Interface, notification, and update options.");
this.cbConfig.UseVisualStyleBackColor = true; this.cbProgramConfig.UseVisualStyleBackColor = true;
this.cbConfig.CheckedChanged += new System.EventHandler(this.cbConfig_CheckedChanged); this.cbProgramConfig.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged);
// //
// cbSession // cbSession
// //
this.cbSession.AutoSize = true; this.cbSession.AutoSize = true;
this.cbSession.Location = new System.Drawing.Point(0, 27); this.cbSession.Location = new System.Drawing.Point(3, 49);
this.cbSession.Name = "cbSession"; this.cbSession.Name = "cbSession";
this.cbSession.Size = new System.Drawing.Size(92, 17); this.cbSession.Size = new System.Drawing.Size(92, 17);
this.cbSession.TabIndex = 1; this.cbSession.TabIndex = 2;
this.cbSession.Text = "Login Session"; this.cbSession.Text = "Login Session";
this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account."); this.toolTip.SetToolTip(this.cbSession, "A token that allows logging into the\r\ncurrent TweetDeck account.");
this.cbSession.UseVisualStyleBackColor = true; this.cbSession.UseVisualStyleBackColor = true;
this.cbSession.CheckedChanged += new System.EventHandler(this.cbSession_CheckedChanged); this.cbSession.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged);
// //
// cbPluginData // cbPluginData
// //
this.cbPluginData.AutoSize = true; this.cbPluginData.AutoSize = true;
this.cbPluginData.Location = new System.Drawing.Point(0, 51); this.cbPluginData.Location = new System.Drawing.Point(3, 72);
this.cbPluginData.Name = "cbPluginData"; this.cbPluginData.Name = "cbPluginData";
this.cbPluginData.Size = new System.Drawing.Size(81, 17); this.cbPluginData.Size = new System.Drawing.Size(81, 17);
this.cbPluginData.TabIndex = 2; this.cbPluginData.TabIndex = 3;
this.cbPluginData.Text = "Plugin Data"; this.cbPluginData.Text = "Plugin Data";
this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves."); this.toolTip.SetToolTip(this.cbPluginData, "Data files generated by plugins.\r\nDoes not include the plugins themselves.");
this.cbPluginData.UseVisualStyleBackColor = true; this.cbPluginData.UseVisualStyleBackColor = true;
this.cbPluginData.CheckedChanged += new System.EventHandler(this.cbPluginData_CheckedChanged); this.cbPluginData.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged);
// //
// panelExport // cbSystemConfig
// //
this.panelExport.Controls.Add(this.cbConfig); this.cbSystemConfig.AutoSize = true;
this.panelExport.Controls.Add(this.cbPluginData); this.cbSystemConfig.Location = new System.Drawing.Point(3, 26);
this.panelExport.Controls.Add(this.cbSession); this.cbSystemConfig.Name = "cbSystemConfig";
this.panelExport.Location = new System.Drawing.Point(12, 12); this.cbSystemConfig.Size = new System.Drawing.Size(99, 17);
this.panelExport.Name = "panelExport"; this.cbSystemConfig.TabIndex = 1;
this.panelExport.Size = new System.Drawing.Size(220, 79); this.cbSystemConfig.Text = "System Options";
this.panelExport.TabIndex = 5; this.toolTip.SetToolTip(this.cbSystemConfig, "Hardware acceleration and cache options.");
this.panelExport.Visible = false; this.cbSystemConfig.UseVisualStyleBackColor = true;
this.cbSystemConfig.CheckedChanged += new System.EventHandler(this.checkBoxSelection_CheckedChanged);
//
// panelSelection
//
this.panelSelection.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelSelection.Controls.Add(this.cbProgramConfig);
this.panelSelection.Controls.Add(this.cbSystemConfig);
this.panelSelection.Controls.Add(this.cbSession);
this.panelSelection.Controls.Add(this.cbPluginData);
this.panelSelection.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.panelSelection.Location = new System.Drawing.Point(12, 12);
this.panelSelection.Name = "panelSelection";
this.panelSelection.Size = new System.Drawing.Size(220, 89);
this.panelSelection.TabIndex = 2;
this.panelSelection.Visible = false;
this.panelSelection.WrapContents = false;
// //
// panelDecision // panelDecision
// //
this.panelDecision.Controls.Add(this.radioReset); this.panelDecision.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.panelDecision.Controls.Add(this.radioExport); | System.Windows.Forms.AnchorStyles.Right)));
this.panelDecision.Controls.Add(this.radioImport); this.panelDecision.Controls.Add(this.radioImport);
this.panelDecision.Controls.Add(this.radioExport);
this.panelDecision.Controls.Add(this.radioReset);
this.panelDecision.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.panelDecision.Location = new System.Drawing.Point(12, 12); this.panelDecision.Location = new System.Drawing.Point(12, 12);
this.panelDecision.Name = "panelDecision"; this.panelDecision.Name = "panelDecision";
this.panelDecision.Size = new System.Drawing.Size(220, 79); this.panelDecision.Size = new System.Drawing.Size(220, 71);
this.panelDecision.TabIndex = 6; this.panelDecision.TabIndex = 0;
// this.panelDecision.WrapContents = false;
// radioReset
//
this.radioReset.AutoSize = true;
this.radioReset.Location = new System.Drawing.Point(3, 49);
this.radioReset.Name = "radioReset";
this.radioReset.Size = new System.Drawing.Size(104, 17);
this.radioReset.TabIndex = 2;
this.radioReset.TabStop = true;
this.radioReset.Text = "Restore Defaults";
this.radioReset.UseVisualStyleBackColor = true;
this.radioReset.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// radioExport
//
this.radioExport.AutoSize = true;
this.radioExport.Location = new System.Drawing.Point(3, 26);
this.radioExport.Name = "radioExport";
this.radioExport.Size = new System.Drawing.Size(87, 17);
this.radioExport.TabIndex = 1;
this.radioExport.TabStop = true;
this.radioExport.Text = "Export Profile";
this.radioExport.UseVisualStyleBackColor = true;
this.radioExport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
// //
// radioImport // radioImport
// //
@@ -158,25 +156,49 @@
this.radioImport.UseVisualStyleBackColor = true; this.radioImport.UseVisualStyleBackColor = true;
this.radioImport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged); this.radioImport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
// //
// radioExport
//
this.radioExport.AutoSize = true;
this.radioExport.Location = new System.Drawing.Point(3, 26);
this.radioExport.Name = "radioExport";
this.radioExport.Size = new System.Drawing.Size(87, 17);
this.radioExport.TabIndex = 1;
this.radioExport.TabStop = true;
this.radioExport.Text = "Export Profile";
this.radioExport.UseVisualStyleBackColor = true;
this.radioExport.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// radioReset
//
this.radioReset.AutoSize = true;
this.radioReset.Location = new System.Drawing.Point(3, 49);
this.radioReset.Name = "radioReset";
this.radioReset.Size = new System.Drawing.Size(104, 17);
this.radioReset.TabIndex = 2;
this.radioReset.TabStop = true;
this.radioReset.Text = "Restore Defaults";
this.radioReset.UseVisualStyleBackColor = true;
this.radioReset.CheckedChanged += new System.EventHandler(this.radioDecision_CheckedChanged);
//
// DialogSettingsManage // DialogSettingsManage
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(244, 132); this.ClientSize = new System.Drawing.Size(244, 132);
this.Controls.Add(this.panelDecision);
this.Controls.Add(this.panelExport);
this.Controls.Add(this.btnContinue); this.Controls.Add(this.btnContinue);
this.Controls.Add(this.btnCancel); this.Controls.Add(this.btnCancel);
this.Controls.Add(this.panelDecision);
this.Controls.Add(this.panelSelection);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(200, 170); this.MinimumSize = new System.Drawing.Size(260, 170);
this.Name = "DialogSettingsManage"; this.Name = "DialogSettingsManage";
this.ShowIcon = false; this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Manage Options"; this.Text = "Manage Options";
this.panelExport.ResumeLayout(false); this.panelSelection.ResumeLayout(false);
this.panelExport.PerformLayout(); this.panelSelection.PerformLayout();
this.panelDecision.ResumeLayout(false); this.panelDecision.ResumeLayout(false);
this.panelDecision.PerformLayout(); this.panelDecision.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
@@ -188,12 +210,13 @@
private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Button btnContinue; private System.Windows.Forms.Button btnContinue;
private System.Windows.Forms.CheckBox cbConfig; private System.Windows.Forms.CheckBox cbProgramConfig;
private System.Windows.Forms.CheckBox cbSystemConfig;
private System.Windows.Forms.CheckBox cbSession; private System.Windows.Forms.CheckBox cbSession;
private System.Windows.Forms.CheckBox cbPluginData; private System.Windows.Forms.CheckBox cbPluginData;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Panel panelExport; private System.Windows.Forms.FlowLayoutPanel panelSelection;
private System.Windows.Forms.Panel panelDecision; private System.Windows.Forms.FlowLayoutPanel panelDecision;
private System.Windows.Forms.RadioButton radioReset; private System.Windows.Forms.RadioButton radioReset;
private System.Windows.Forms.RadioButton radioExport; private System.Windows.Forms.RadioButton radioExport;
private System.Windows.Forms.RadioButton radioImport; private System.Windows.Forms.RadioButton radioImport;

View File

@@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Other.Settings.Export; using TweetDuck.Core.Management;
using TweetDuck.Plugins; using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
@@ -11,46 +12,47 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Deciding, Reset, Import, Export Deciding, Reset, Import, Export
} }
public ExportFileFlags Flags{ private ProfileManager.Items SelectedItems{
get => selectedFlags; get => _selectedItems;
set{ set{
// this will call events and SetFlag, which also updates the UI // this will call events and SetFlag, which also updates the UI
cbConfig.Checked = value.HasFlag(ExportFileFlags.UserConfig); foreach(KeyValuePair<CheckBox, ProfileManager.Items> kvp in checkBoxMap){
cbSession.Checked = value.HasFlag(ExportFileFlags.Session); kvp.Key.Checked = value.HasFlag(kvp.Value);
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData); }
} }
} }
public bool IsRestarting { get; private set; }
public bool ShouldReloadBrowser { get; private set; } public bool ShouldReloadBrowser { get; private set; }
private readonly PluginManager plugins; private readonly PluginManager plugins;
private State currentState; private readonly Dictionary<CheckBox, ProfileManager.Items> checkBoxMap = new Dictionary<CheckBox, ProfileManager.Items>(4);
private ExportManager importManager; private State currentState;
private ExportFileFlags selectedFlags = ExportFileFlags.None; private ProfileManager importManager;
private ProfileManager.Items _selectedItems = ProfileManager.Items.None;
public DialogSettingsManage(PluginManager plugins){ public DialogSettingsManage(PluginManager plugins){
InitializeComponent(); InitializeComponent();
this.plugins = plugins; this.plugins = plugins;
this.currentState = State.Deciding; this.currentState = State.Deciding;
this.checkBoxMap[cbProgramConfig] = ProfileManager.Items.UserConfig;
this.checkBoxMap[cbSystemConfig] = ProfileManager.Items.SystemConfig;
this.checkBoxMap[cbSession] = ProfileManager.Items.Session;
this.checkBoxMap[cbPluginData] = ProfileManager.Items.PluginData;
} }
private void radioDecision_CheckedChanged(object sender, EventArgs e){ private void radioDecision_CheckedChanged(object sender, EventArgs e){
btnContinue.Enabled = true; btnContinue.Enabled = true;
} }
private void cbConfig_CheckedChanged(object sender, EventArgs e){ private void checkBoxSelection_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.UserConfig, cbConfig.Checked); CheckBox cb = (CheckBox)sender;
} SetFlag(checkBoxMap[cb], cb.Checked);
private void cbSession_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Session, cbSession.Checked);
}
private void cbPluginData_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.PluginData, cbPluginData.Checked);
} }
private void btnContinue_Click(object sender, EventArgs e){ private void btnContinue_Click(object sender, EventArgs e){
@@ -63,7 +65,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
currentState = State.Reset; currentState = State.Reset;
Text = "Restore Defaults"; Text = "Restore Defaults";
Flags = ExportFileFlags.UserConfig; SelectedItems = ProfileManager.Items.UserConfig;
} }
// Import // Import
@@ -81,15 +83,15 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
file = dialog.FileName; file = dialog.FileName;
} }
importManager = new ExportManager(file, plugins); importManager = new ProfileManager(file, plugins);
currentState = State.Import; currentState = State.Import;
Text = "Import Profile"; Text = "Import Profile";
Flags = importManager.GetImportFlags(); SelectedItems = importManager.FindImportItems();
cbConfig.Enabled = cbConfig.Checked; foreach(CheckBox cb in checkBoxMap.Keys){
cbSession.Enabled = cbSession.Checked; cb.Enabled = cb.Checked;
cbPluginData.Enabled = cbPluginData.Checked; }
} }
// Export // Export
@@ -98,21 +100,22 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Text = "Export Profile"; Text = "Export Profile";
btnContinue.Text = "Export Profile"; btnContinue.Text = "Export Profile";
Flags = ExportFileFlags.All & ~ExportFileFlags.Session; SelectedItems = ProfileManager.Items.UserConfig | ProfileManager.Items.PluginData;
} }
// Continue... // Continue...
panelDecision.Visible = false; panelDecision.Visible = false;
panelExport.Visible = true; panelSelection.Visible = true;
Height += panelSelection.Height-panelDecision.Height;
break; break;
case State.Reset: case State.Reset:
if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Warning("Reset TweetDuck Options", "This will reset the selected items. Are you sure you want to proceed?", FormMessage.Yes, FormMessage.No)){
if (Flags.HasFlag(ExportFileFlags.UserConfig)){ if (SelectedItems.HasFlag(ProfileManager.Items.UserConfig)){
Program.ResetConfig(); Program.UserConfig.Reset();
} }
if (Flags.HasFlag(ExportFileFlags.SystemConfig)){ if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){
try{ try{
File.Delete(Program.SystemConfigFilePath); File.Delete(Program.SystemConfigFilePath);
}catch(Exception ex){ }catch(Exception ex){
@@ -120,7 +123,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
} }
if (Flags.HasFlag(ExportFileFlags.PluginData)){ if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)){
try{ try{
File.Delete(Program.PluginConfigFilePath); File.Delete(Program.PluginConfigFilePath);
Directory.Delete(Program.PluginDataPath, true); Directory.Delete(Program.PluginDataPath, true);
@@ -129,11 +132,11 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
} }
if (Flags.HasFlag(ExportFileFlags.Session)){ if (SelectedItems.HasFlag(ProfileManager.Items.Session)){
Program.Restart(Arguments.ArgDeleteCookies); RestartProgram(Arguments.ArgDeleteCookies);
} }
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){ else if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){
Program.Restart(); RestartProgram();
} }
else{ else{
ShouldReloadBrowser = true; ShouldReloadBrowser = true;
@@ -146,15 +149,15 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
break; break;
case State.Import: case State.Import:
if (importManager.Import(Flags)){ if (importManager.Import(SelectedItems)){
Program.UserConfig.Reload(); Program.UserConfig.Reload();
if (importManager.IsRestarting){ if (importManager.IsRestarting){
if (Flags.HasFlag(ExportFileFlags.Session)){ if (SelectedItems.HasFlag(ProfileManager.Items.Session)){
Program.Restart(Arguments.ArgImportCookies); RestartProgram(Arguments.ArgImportCookies);
} }
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){ else if (SelectedItems.HasFlag(ProfileManager.Items.SystemConfig)){
Program.Restart(); RestartProgram();
} }
} }
else{ else{
@@ -189,9 +192,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Program.UserConfig.Save(); Program.UserConfig.Save();
Program.SystemConfig.Save(); Program.SystemConfig.Save();
ExportManager manager = new ExportManager(file, plugins); ProfileManager manager = new ProfileManager(file, plugins);
if (!manager.Export(Flags)){ if (!manager.Export(SelectedItems)){
Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException); Program.Reporter.HandleException("Profile Export Error", "An exception happened while exporting TweetDuck profile.", true, manager.LastException);
} }
@@ -206,16 +209,21 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Close(); Close();
} }
private void SetFlag(ExportFileFlags flag, bool enable){ private void SetFlag(ProfileManager.Items flag, bool enable){
selectedFlags = enable ? selectedFlags | flag : selectedFlags & ~flag; _selectedItems = enable ? _selectedItems | flag : _selectedItems & ~flag;
btnContinue.Enabled = selectedFlags != ExportFileFlags.None; btnContinue.Enabled = _selectedItems != ProfileManager.Items.None;
if (currentState == State.Import){ if (currentState == State.Import){
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Import && Restart" : "Import Profile"; btnContinue.Text = _selectedItems.NeedsRestart() ? "Import && Restart" : "Import Profile";
} }
else if (currentState == State.Reset){ else if (currentState == State.Reset){
btnContinue.Text = selectedFlags.HasFlag(ExportFileFlags.Session) ? "Restore && Restart" : "Restore Defaults"; btnContinue.Text = _selectedItems.NeedsRestart() ? "Restore && Restart" : "Restore Defaults";
} }
}
private void RestartProgram(params string[] extraArgs){
IsRestarting = true;
Program.Restart(extraArgs);
} }
} }
} }

View File

@@ -1,13 +0,0 @@
using System;
namespace TweetDuck.Core.Other.Settings.Export{
[Flags]
enum ExportFileFlags{
None = 0,
UserConfig = 1,
SystemConfig = 2, // TODO implement later
Session = 4,
PluginData = 8,
All = UserConfig|SystemConfig|Session|PluginData
}
}

View File

@@ -56,7 +56,7 @@
this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.btnClearCache.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnClearCache.Name = "btnClearCache"; this.btnClearCache.Name = "btnClearCache";
this.btnClearCache.Size = new System.Drawing.Size(144, 23); this.btnClearCache.Size = new System.Drawing.Size(144, 23);
this.btnClearCache.TabIndex = 1; this.btnClearCache.TabIndex = 5;
this.btnClearCache.Text = "Clear Cache (calculating)"; this.btnClearCache.Text = "Clear Cache (calculating)";
this.btnClearCache.UseVisualStyleBackColor = true; this.btnClearCache.UseVisualStyleBackColor = true;
// //
@@ -67,7 +67,7 @@
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration"; this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17); this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
this.checkHardwareAcceleration.TabIndex = 0; this.checkHardwareAcceleration.TabIndex = 3;
this.checkHardwareAcceleration.Text = "Hardware Acceleration"; this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true; this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
// //
@@ -136,7 +136,7 @@
this.numClearCacheThreshold.Minimum = 100; this.numClearCacheThreshold.Minimum = 100;
this.numClearCacheThreshold.Name = "numClearCacheThreshold"; this.numClearCacheThreshold.Name = "numClearCacheThreshold";
this.numClearCacheThreshold.Size = new System.Drawing.Size(72, 20); this.numClearCacheThreshold.Size = new System.Drawing.Size(72, 20);
this.numClearCacheThreshold.TabIndex = 4; this.numClearCacheThreshold.TabIndex = 1;
this.numClearCacheThreshold.TextSuffix = " MB"; this.numClearCacheThreshold.TextSuffix = " MB";
this.numClearCacheThreshold.Value = 250; this.numClearCacheThreshold.Value = 250;
// //
@@ -147,7 +147,7 @@
this.checkClearCacheAuto.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkClearCacheAuto.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkClearCacheAuto.Name = "checkClearCacheAuto"; this.checkClearCacheAuto.Name = "checkClearCacheAuto";
this.checkClearCacheAuto.Size = new System.Drawing.Size(215, 17); this.checkClearCacheAuto.Size = new System.Drawing.Size(215, 17);
this.checkClearCacheAuto.TabIndex = 3; this.checkClearCacheAuto.TabIndex = 0;
this.checkClearCacheAuto.Text = "Clear Cache Automatically When Above"; this.checkClearCacheAuto.Text = "Clear Cache Automatically When Above";
this.checkClearCacheAuto.UseVisualStyleBackColor = true; this.checkClearCacheAuto.UseVisualStyleBackColor = true;
// //
@@ -195,7 +195,7 @@
this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0); this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0);
this.panelClearCacheAuto.Name = "panelClearCacheAuto"; this.panelClearCacheAuto.Name = "panelClearCacheAuto";
this.panelClearCacheAuto.Size = new System.Drawing.Size(322, 26); this.panelClearCacheAuto.Size = new System.Drawing.Size(322, 26);
this.panelClearCacheAuto.TabIndex = 3; this.panelClearCacheAuto.TabIndex = 6;
// //
// labelCache // labelCache
// //
@@ -204,7 +204,7 @@
this.labelCache.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelCache.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelCache.Name = "labelCache"; this.labelCache.Name = "labelCache";
this.labelCache.Size = new System.Drawing.Size(38, 13); this.labelCache.Size = new System.Drawing.Size(38, 13);
this.labelCache.TabIndex = 2; this.labelCache.TabIndex = 4;
this.labelCache.Text = "Cache"; this.labelCache.Text = "Cache";
// //
// panelConfiguration // panelConfiguration
@@ -216,7 +216,7 @@
this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0); this.panelConfiguration.Margin = new System.Windows.Forms.Padding(0);
this.panelConfiguration.Name = "panelConfiguration"; this.panelConfiguration.Name = "panelConfiguration";
this.panelConfiguration.Size = new System.Drawing.Size(322, 29); this.panelConfiguration.Size = new System.Drawing.Size(322, 29);
this.panelConfiguration.TabIndex = 5; this.panelConfiguration.TabIndex = 8;
// //
// labelConfiguration // labelConfiguration
// //
@@ -226,7 +226,7 @@
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelConfiguration.Name = "labelConfiguration"; this.labelConfiguration.Name = "labelConfiguration";
this.labelConfiguration.Size = new System.Drawing.Size(104, 20); this.labelConfiguration.Size = new System.Drawing.Size(104, 20);
this.labelConfiguration.TabIndex = 4; this.labelConfiguration.TabIndex = 7;
this.labelConfiguration.Text = "Configuration"; this.labelConfiguration.Text = "Configuration";
// //
// flowPanel // flowPanel
@@ -247,7 +247,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 295); this.flowPanel.Size = new System.Drawing.Size(322, 295);
this.flowPanel.TabIndex = 6; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// TabSettingsAdvanced // TabSettingsAdvanced

View File

@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Management;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;

View File

@@ -47,7 +47,7 @@
this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0); this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0);
this.panelDataCollection.Name = "panelDataCollection"; this.panelDataCollection.Name = "panelDataCollection";
this.panelDataCollection.Size = new System.Drawing.Size(322, 26); this.panelDataCollection.Size = new System.Drawing.Size(322, 26);
this.panelDataCollection.TabIndex = 1; this.panelDataCollection.TabIndex = 3;
// //
// labelDataCollectionLink // labelDataCollectionLink
// //
@@ -58,9 +58,10 @@
this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelDataCollectionLink.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelDataCollectionLink.Name = "labelDataCollectionLink"; this.labelDataCollectionLink.Name = "labelDataCollectionLink";
this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17); this.labelDataCollectionLink.Size = new System.Drawing.Size(66, 17);
this.labelDataCollectionLink.TabIndex = 3; this.labelDataCollectionLink.TabIndex = 1;
this.labelDataCollectionLink.TabStop = true; this.labelDataCollectionLink.TabStop = true;
this.labelDataCollectionLink.Text = "(learn more)"; this.labelDataCollectionLink.Text = "(learn more)";
this.labelDataCollectionLink.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.labelDataCollectionLink.UseCompatibleTextRendering = true; this.labelDataCollectionLink.UseCompatibleTextRendering = true;
// //
// checkDataCollection // checkDataCollection
@@ -70,7 +71,7 @@
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 3); this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 6, 0, 3);
this.checkDataCollection.Name = "checkDataCollection"; this.checkDataCollection.Name = "checkDataCollection";
this.checkDataCollection.Size = new System.Drawing.Size(135, 17); this.checkDataCollection.Size = new System.Drawing.Size(135, 17);
this.checkDataCollection.TabIndex = 2; this.checkDataCollection.TabIndex = 0;
this.checkDataCollection.Text = "Send Anonymous Data"; this.checkDataCollection.Text = "Send Anonymous Data";
this.checkDataCollection.UseVisualStyleBackColor = true; this.checkDataCollection.UseVisualStyleBackColor = true;
// //
@@ -101,7 +102,7 @@
this.btnSendFeedback.Name = "btnSendFeedback"; this.btnSendFeedback.Name = "btnSendFeedback";
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23); this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
this.btnSendFeedback.TabIndex = 0; this.btnSendFeedback.TabIndex = 1;
this.btnSendFeedback.Text = "Send Feedback / Bug Report"; this.btnSendFeedback.Text = "Send Feedback / Bug Report";
this.btnSendFeedback.UseVisualStyleBackColor = true; this.btnSendFeedback.UseVisualStyleBackColor = true;
// //
@@ -112,7 +113,7 @@
this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelDataCollection.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDataCollection.Name = "labelDataCollection"; this.labelDataCollection.Name = "labelDataCollection";
this.labelDataCollection.Size = new System.Drawing.Size(79, 13); this.labelDataCollection.Size = new System.Drawing.Size(79, 13);
this.labelDataCollection.TabIndex = 1; this.labelDataCollection.TabIndex = 2;
this.labelDataCollection.Text = "Data Collection"; this.labelDataCollection.Text = "Data Collection";
// //
// labelFeedback // labelFeedback
@@ -141,7 +142,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 209); this.flowPanel.Size = new System.Drawing.Size(322, 209);
this.flowPanel.TabIndex = 2; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// TabSettingsFeedback // TabSettingsFeedback

View File

@@ -41,6 +41,10 @@
this.labelUpdates = new System.Windows.Forms.Label(); this.labelUpdates = new System.Windows.Forms.Label();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox(); this.checkKeepLikeFollowDialogsOpen = new System.Windows.Forms.CheckBox();
this.labelBrowserSettings = new System.Windows.Forms.Label();
this.checkSmoothScrolling = new System.Windows.Forms.CheckBox();
this.labelBrowserPath = new System.Windows.Forms.Label();
this.comboBoxBrowserPath = new System.Windows.Forms.ComboBox();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.panelZoom.SuspendLayout(); this.panelZoom.SuspendLayout();
this.flowPanel.SuspendLayout(); this.flowPanel.SuspendLayout();
@@ -53,28 +57,28 @@
this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkExpandLinks.Name = "checkExpandLinks"; this.checkExpandLinks.Name = "checkExpandLinks";
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17); this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
this.checkExpandLinks.TabIndex = 0; this.checkExpandLinks.TabIndex = 1;
this.checkExpandLinks.Text = "Expand Links When Hovered"; this.checkExpandLinks.Text = "Expand Links When Hovered";
this.checkExpandLinks.UseVisualStyleBackColor = true; this.checkExpandLinks.UseVisualStyleBackColor = true;
// //
// checkUpdateNotifications // checkUpdateNotifications
// //
this.checkUpdateNotifications.AutoSize = true; this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 268); this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 386);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkUpdateNotifications.Name = "checkUpdateNotifications"; this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17); this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
this.checkUpdateNotifications.TabIndex = 0; this.checkUpdateNotifications.TabIndex = 14;
this.checkUpdateNotifications.Text = "Check Updates Automatically"; this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.checkUpdateNotifications.UseVisualStyleBackColor = true; this.checkUpdateNotifications.UseVisualStyleBackColor = true;
// //
// btnCheckUpdates // btnCheckUpdates
// //
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 291); this.btnCheckUpdates.Location = new System.Drawing.Point(5, 409);
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.Size = new System.Drawing.Size(144, 23); this.btnCheckUpdates.Size = new System.Drawing.Size(144, 23);
this.btnCheckUpdates.TabIndex = 1; this.btnCheckUpdates.TabIndex = 15;
this.btnCheckUpdates.Text = "Check Updates Now"; this.btnCheckUpdates.Text = "Check Updates Now";
this.btnCheckUpdates.UseVisualStyleBackColor = true; this.btnCheckUpdates.UseVisualStyleBackColor = true;
// //
@@ -85,7 +89,7 @@
this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelZoomValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelZoomValue.Name = "labelZoomValue"; this.labelZoomValue.Name = "labelZoomValue";
this.labelZoomValue.Size = new System.Drawing.Size(38, 13); this.labelZoomValue.Size = new System.Drawing.Size(38, 13);
this.labelZoomValue.TabIndex = 8; this.labelZoomValue.TabIndex = 1;
this.labelZoomValue.Text = "100%"; this.labelZoomValue.Text = "100%";
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
@@ -96,7 +100,7 @@
this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSwitchAccountSelectors.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors"; this.checkSwitchAccountSelectors.Name = "checkSwitchAccountSelectors";
this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17); this.checkSwitchAccountSelectors.Size = new System.Drawing.Size(172, 17);
this.checkSwitchAccountSelectors.TabIndex = 1; this.checkSwitchAccountSelectors.TabIndex = 2;
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts"; this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true; this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
// //
@@ -107,7 +111,7 @@
this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkBestImageQuality.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkBestImageQuality.Name = "checkBestImageQuality"; this.checkBestImageQuality.Name = "checkBestImageQuality";
this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17); this.checkBestImageQuality.Size = new System.Drawing.Size(114, 17);
this.checkBestImageQuality.TabIndex = 3; this.checkBestImageQuality.TabIndex = 5;
this.checkBestImageQuality.Text = "Best Image Quality"; this.checkBestImageQuality.Text = "Best Image Quality";
this.checkBestImageQuality.UseVisualStyleBackColor = true; this.checkBestImageQuality.UseVisualStyleBackColor = true;
// //
@@ -118,7 +122,7 @@
this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkOpenSearchInFirstColumn.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn"; this.checkOpenSearchInFirstColumn.Name = "checkOpenSearchInFirstColumn";
this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17); this.checkOpenSearchInFirstColumn.Size = new System.Drawing.Size(219, 17);
this.checkOpenSearchInFirstColumn.TabIndex = 2; this.checkOpenSearchInFirstColumn.TabIndex = 3;
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column"; this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true; this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
// //
@@ -133,18 +137,18 @@
this.trackBarZoom.Name = "trackBarZoom"; this.trackBarZoom.Name = "trackBarZoom";
this.trackBarZoom.Size = new System.Drawing.Size(148, 30); this.trackBarZoom.Size = new System.Drawing.Size(148, 30);
this.trackBarZoom.SmallChange = 5; this.trackBarZoom.SmallChange = 5;
this.trackBarZoom.TabIndex = 7; this.trackBarZoom.TabIndex = 0;
this.trackBarZoom.TickFrequency = 25; this.trackBarZoom.TickFrequency = 25;
this.trackBarZoom.Value = 100; this.trackBarZoom.Value = 100;
// //
// labelZoom // labelZoom
// //
this.labelZoom.AutoSize = true; this.labelZoom.AutoSize = true;
this.labelZoom.Location = new System.Drawing.Point(3, 173); this.labelZoom.Location = new System.Drawing.Point(3, 291);
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(34, 13); this.labelZoom.Size = new System.Drawing.Size(34, 13);
this.labelZoom.TabIndex = 6; this.labelZoom.TabIndex = 11;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// zoomUpdateTimer // zoomUpdateTimer
@@ -168,11 +172,11 @@
this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top; this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top;
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, 186); this.panelZoom.Location = new System.Drawing.Point(0, 304);
this.panelZoom.Margin = new System.Windows.Forms.Padding(0); this.panelZoom.Margin = new System.Windows.Forms.Padding(0);
this.panelZoom.Name = "panelZoom"; this.panelZoom.Name = "panelZoom";
this.panelZoom.Size = new System.Drawing.Size(322, 36); this.panelZoom.Size = new System.Drawing.Size(322, 36);
this.panelZoom.TabIndex = 1; this.panelZoom.TabIndex = 12;
// //
// checkAnimatedAvatars // checkAnimatedAvatars
// //
@@ -181,7 +185,7 @@
this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkAnimatedAvatars.Name = "checkAnimatedAvatars"; this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17); this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
this.checkAnimatedAvatars.TabIndex = 4; this.checkAnimatedAvatars.TabIndex = 6;
this.checkAnimatedAvatars.Text = "Enable Animated Avatars"; this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
this.checkAnimatedAvatars.UseVisualStyleBackColor = true; this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
// //
@@ -189,11 +193,11 @@
// //
this.labelUpdates.AutoSize = true; this.labelUpdates.AutoSize = true;
this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelUpdates.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUpdates.Location = new System.Drawing.Point(0, 242); this.labelUpdates.Location = new System.Drawing.Point(0, 360);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelUpdates.Name = "labelUpdates"; this.labelUpdates.Name = "labelUpdates";
this.labelUpdates.Size = new System.Drawing.Size(70, 20); this.labelUpdates.Size = new System.Drawing.Size(70, 20);
this.labelUpdates.TabIndex = 2; this.labelUpdates.TabIndex = 13;
this.labelUpdates.Text = "Updates"; this.labelUpdates.Text = "Updates";
// //
// flowPanel // flowPanel
@@ -208,6 +212,10 @@
this.flowPanel.Controls.Add(this.checkKeepLikeFollowDialogsOpen); this.flowPanel.Controls.Add(this.checkKeepLikeFollowDialogsOpen);
this.flowPanel.Controls.Add(this.checkBestImageQuality); this.flowPanel.Controls.Add(this.checkBestImageQuality);
this.flowPanel.Controls.Add(this.checkAnimatedAvatars); this.flowPanel.Controls.Add(this.checkAnimatedAvatars);
this.flowPanel.Controls.Add(this.labelBrowserSettings);
this.flowPanel.Controls.Add(this.checkSmoothScrolling);
this.flowPanel.Controls.Add(this.labelBrowserPath);
this.flowPanel.Controls.Add(this.comboBoxBrowserPath);
this.flowPanel.Controls.Add(this.labelZoom); this.flowPanel.Controls.Add(this.labelZoom);
this.flowPanel.Controls.Add(this.panelZoom); this.flowPanel.Controls.Add(this.panelZoom);
this.flowPanel.Controls.Add(this.labelUpdates); this.flowPanel.Controls.Add(this.labelUpdates);
@@ -216,8 +224,8 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 319); this.flowPanel.Size = new System.Drawing.Size(322, 438);
this.flowPanel.TabIndex = 4; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// checkKeepLikeFollowDialogsOpen // checkKeepLikeFollowDialogsOpen
@@ -227,17 +235,59 @@
this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkKeepLikeFollowDialogsOpen.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen"; this.checkKeepLikeFollowDialogsOpen.Name = "checkKeepLikeFollowDialogsOpen";
this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(176, 17); this.checkKeepLikeFollowDialogsOpen.Size = new System.Drawing.Size(176, 17);
this.checkKeepLikeFollowDialogsOpen.TabIndex = 7; this.checkKeepLikeFollowDialogsOpen.TabIndex = 4;
this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open"; this.checkKeepLikeFollowDialogsOpen.Text = "Keep Like/Follow Dialogs Open";
this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true; this.checkKeepLikeFollowDialogsOpen.UseVisualStyleBackColor = true;
// //
// labelBrowserSettings
//
this.labelBrowserSettings.AutoSize = true;
this.labelBrowserSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelBrowserSettings.Location = new System.Drawing.Point(0, 181);
this.labelBrowserSettings.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelBrowserSettings.Name = "labelBrowserSettings";
this.labelBrowserSettings.Size = new System.Drawing.Size(130, 20);
this.labelBrowserSettings.TabIndex = 7;
this.labelBrowserSettings.Text = "Browser Settings";
//
// checkSmoothScrolling
//
this.checkSmoothScrolling.AutoSize = true;
this.checkSmoothScrolling.Location = new System.Drawing.Point(6, 207);
this.checkSmoothScrolling.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkSmoothScrolling.Name = "checkSmoothScrolling";
this.checkSmoothScrolling.Size = new System.Drawing.Size(105, 17);
this.checkSmoothScrolling.TabIndex = 8;
this.checkSmoothScrolling.Text = "Smooth Scrolling";
this.checkSmoothScrolling.UseVisualStyleBackColor = true;
//
// labelBrowserPath
//
this.labelBrowserPath.AutoSize = true;
this.labelBrowserPath.Location = new System.Drawing.Point(3, 239);
this.labelBrowserPath.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelBrowserPath.Name = "labelBrowserPath";
this.labelBrowserPath.Size = new System.Drawing.Size(95, 13);
this.labelBrowserPath.TabIndex = 9;
this.labelBrowserPath.Text = "Open Links With...";
//
// comboBoxBrowserPath
//
this.comboBoxBrowserPath.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxBrowserPath.FormattingEnabled = true;
this.comboBoxBrowserPath.Location = new System.Drawing.Point(5, 255);
this.comboBoxBrowserPath.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxBrowserPath.Name = "comboBoxBrowserPath";
this.comboBoxBrowserPath.Size = new System.Drawing.Size(173, 21);
this.comboBoxBrowserPath.TabIndex = 10;
//
// TabSettingsGeneral // TabSettingsGeneral
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.flowPanel); this.Controls.Add(this.flowPanel);
this.Name = "TabSettingsGeneral"; this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(340, 337); this.Size = new System.Drawing.Size(340, 456);
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
this.panelZoom.ResumeLayout(false); this.panelZoom.ResumeLayout(false);
this.flowPanel.ResumeLayout(false); this.flowPanel.ResumeLayout(false);
@@ -265,5 +315,9 @@
private System.Windows.Forms.CheckBox checkAnimatedAvatars; private System.Windows.Forms.CheckBox checkAnimatedAvatars;
private System.Windows.Forms.FlowLayoutPanel flowPanel; private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen; private System.Windows.Forms.CheckBox checkKeepLikeFollowDialogsOpen;
private System.Windows.Forms.Label labelBrowserPath;
private System.Windows.Forms.ComboBox comboBoxBrowserPath;
private System.Windows.Forms.Label labelBrowserSettings;
private System.Windows.Forms.CheckBox checkSmoothScrolling;
} }
} }

View File

@@ -1,6 +1,10 @@
using System; using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils;
using TweetDuck.Updates; using TweetDuck.Updates;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
@@ -9,6 +13,9 @@ namespace TweetDuck.Core.Other.Settings{
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private int updateCheckEventId = -1; private int updateCheckEventId = -1;
private readonly int browserListIndexDefault;
private readonly int browserListIndexCustom;
public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){ public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
InitializeComponent(); InitializeComponent();
@@ -25,15 +32,14 @@ namespace TweetDuck.Core.Other.Settings{
toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL)."); toolTip.SetToolTip(checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
toolTip.SetToolTip(checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs."); toolTip.SetToolTip(checkAnimatedAvatars, "Some old Twitter avatars could be uploaded as animated GIFs.");
toolTip.SetToolTip(checkSmoothScrolling, "Toggles smooth mouse wheel scrolling.");
toolTip.SetToolTip(comboBoxBrowserPath, "Sets the default browser for opening links.");
toolTip.SetToolTip(labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots."); toolTip.SetToolTip(labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue)); toolTip.SetToolTip(trackBarZoom, toolTip.GetToolTip(labelZoomValue));
toolTip.SetToolTip(checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again."); toolTip.SetToolTip(checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
toolTip.SetToolTip(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed."); toolTip.SetToolTip(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%";
checkExpandLinks.Checked = Config.ExpandLinksOnHover; checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors; checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn; checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
@@ -41,6 +47,19 @@ namespace TweetDuck.Core.Other.Settings{
checkBestImageQuality.Checked = Config.BestImageQuality; checkBestImageQuality.Checked = Config.BestImageQuality;
checkAnimatedAvatars.Checked = Config.EnableAnimatedImages; checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
checkSmoothScrolling.Checked = Config.EnableSmoothScrolling;
foreach(WindowsUtils.Browser browserInfo in WindowsUtils.FindInstalledBrowsers()){
comboBoxBrowserPath.Items.Add(browserInfo);
}
browserListIndexDefault = comboBoxBrowserPath.Items.Add("(default browser)");
browserListIndexCustom = comboBoxBrowserPath.Items.Add("(custom program...)");
UpdateBrowserPathSelection();
trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%";
checkUpdateNotifications.Checked = Config.EnableUpdateCheck; checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
} }
@@ -51,6 +70,9 @@ namespace TweetDuck.Core.Other.Settings{
checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged; checkKeepLikeFollowDialogsOpen.CheckedChanged += checkKeepLikeFollowDialogsOpen_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged; checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged; checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
checkSmoothScrolling.CheckedChanged += checkSmoothScrolling_CheckedChanged;
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
@@ -86,6 +108,51 @@ namespace TweetDuck.Core.Other.Settings{
BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns()); BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns());
} }
private void checkSmoothScrolling_CheckedChanged(object sender, EventArgs e){
Config.EnableSmoothScrolling = checkSmoothScrolling.Checked;
PromptRestart();
}
private void UpdateBrowserPathSelection(){
if (string.IsNullOrEmpty(Config.BrowserPath) || !File.Exists(Config.BrowserPath)){
comboBoxBrowserPath.SelectedIndex = browserListIndexDefault;
}
else{
WindowsUtils.Browser browserInfo = comboBoxBrowserPath.Items.OfType<WindowsUtils.Browser>().FirstOrDefault(browser => browser.Path == Config.BrowserPath);
if (browserInfo == null){
comboBoxBrowserPath.SelectedIndex = browserListIndexCustom;
}
else{
comboBoxBrowserPath.SelectedItem = browserInfo;
}
}
}
private void comboBoxBrowserPath_SelectedIndexChanged(object sender, EventArgs e){
if (comboBoxBrowserPath.SelectedIndex == browserListIndexCustom){
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
InitialDirectory = Path.GetDirectoryName(Config.BrowserPath), // returns null if argument is null
Title = "Open Links With...",
Filter = "Executables (*.exe;*.bat;*.cmd)|*.exe;*.bat;*.cmd|All Files (*.*)|*.*"
}){
if (dialog.ShowDialog() == DialogResult.OK){
Config.BrowserPath = dialog.FileName;
}
else{
comboBoxBrowserPath.SelectedIndexChanged -= comboBoxBrowserPath_SelectedIndexChanged;
UpdateBrowserPathSelection();
comboBoxBrowserPath.SelectedIndexChanged += comboBoxBrowserPath_SelectedIndexChanged;
}
}
}
else{
Config.BrowserPath = (comboBoxBrowserPath.SelectedItem as WindowsUtils.Browser)?.Path; // default browser item is a string and casts to null
}
}
private void trackBarZoom_ValueChanged(object sender, EventArgs e){ private void trackBarZoom_ValueChanged(object sender, EventArgs e){
if (trackBarZoom.AlignValueToTick()){ if (trackBarZoom.AlignValueToTick()){
zoomUpdateTimer.Stop(); zoomUpdateTimer.Stop();

View File

@@ -43,7 +43,7 @@
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck"; this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17); this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 5; this.checkSpellCheck.TabIndex = 1;
this.checkSpellCheck.Text = "Enable Spell Check"; this.checkSpellCheck.Text = "Enable Spell Check";
this.checkSpellCheck.UseVisualStyleBackColor = true; this.checkSpellCheck.UseVisualStyleBackColor = true;
// //
@@ -74,7 +74,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 193); this.flowPanel.Size = new System.Drawing.Size(322, 193);
this.flowPanel.TabIndex = 4; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// labelSpellCheckLanguage // labelSpellCheckLanguage
@@ -84,7 +84,7 @@
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(115, 13); this.labelSpellCheckLanguage.Size = new System.Drawing.Size(115, 13);
this.labelSpellCheckLanguage.TabIndex = 11; this.labelSpellCheckLanguage.TabIndex = 2;
this.labelSpellCheckLanguage.Text = "Spell Check Language"; this.labelSpellCheckLanguage.Text = "Spell Check Language";
// //
// comboBoxSpellCheckLanguage // comboBoxSpellCheckLanguage
@@ -95,7 +95,7 @@
this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.comboBoxSpellCheckLanguage.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage"; this.comboBoxSpellCheckLanguage.Name = "comboBoxSpellCheckLanguage";
this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(311, 21); this.comboBoxSpellCheckLanguage.Size = new System.Drawing.Size(311, 21);
this.comboBoxSpellCheckLanguage.TabIndex = 9; this.comboBoxSpellCheckLanguage.TabIndex = 3;
// //
// labelTranslations // labelTranslations
// //
@@ -105,7 +105,7 @@
this.labelTranslations.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelTranslations.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelTranslations.Name = "labelTranslations"; this.labelTranslations.Name = "labelTranslations";
this.labelTranslations.Size = new System.Drawing.Size(116, 20); this.labelTranslations.Size = new System.Drawing.Size(116, 20);
this.labelTranslations.TabIndex = 10; this.labelTranslations.TabIndex = 4;
this.labelTranslations.Text = "Bing Translator"; this.labelTranslations.Text = "Bing Translator";
// //
// labelTranslationTarget // labelTranslationTarget
@@ -115,7 +115,7 @@
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(89, 13); this.labelTranslationTarget.Size = new System.Drawing.Size(89, 13);
this.labelTranslationTarget.TabIndex = 8; this.labelTranslationTarget.TabIndex = 5;
this.labelTranslationTarget.Text = "Target Language"; this.labelTranslationTarget.Text = "Target Language";
// //
// comboBoxTranslationTarget // comboBoxTranslationTarget
@@ -126,7 +126,7 @@
this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget"; this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget";
this.comboBoxTranslationTarget.Size = new System.Drawing.Size(311, 21); this.comboBoxTranslationTarget.Size = new System.Drawing.Size(311, 21);
this.comboBoxTranslationTarget.TabIndex = 7; this.comboBoxTranslationTarget.TabIndex = 6;
// //
// TabSettingsLocales // TabSettingsLocales
// //

View File

@@ -84,7 +84,7 @@
this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelEdgeDistanceValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue"; this.labelEdgeDistanceValue.Name = "labelEdgeDistanceValue";
this.labelEdgeDistanceValue.Size = new System.Drawing.Size(40, 13); this.labelEdgeDistanceValue.Size = new System.Drawing.Size(40, 13);
this.labelEdgeDistanceValue.TabIndex = 9; this.labelEdgeDistanceValue.TabIndex = 1;
this.labelEdgeDistanceValue.Text = "0 px"; this.labelEdgeDistanceValue.Text = "0 px";
this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelEdgeDistanceValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
@@ -95,7 +95,7 @@
this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDisplay.Name = "labelDisplay"; this.labelDisplay.Name = "labelDisplay";
this.labelDisplay.Size = new System.Drawing.Size(41, 13); this.labelDisplay.Size = new System.Drawing.Size(41, 13);
this.labelDisplay.TabIndex = 5; this.labelDisplay.TabIndex = 15;
this.labelDisplay.Text = "Display"; this.labelDisplay.Text = "Display";
// //
// comboBoxDisplay // comboBoxDisplay
@@ -106,7 +106,7 @@
this.comboBoxDisplay.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.comboBoxDisplay.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxDisplay.Name = "comboBoxDisplay"; this.comboBoxDisplay.Name = "comboBoxDisplay";
this.comboBoxDisplay.Size = new System.Drawing.Size(144, 21); this.comboBoxDisplay.Size = new System.Drawing.Size(144, 21);
this.comboBoxDisplay.TabIndex = 6; this.comboBoxDisplay.TabIndex = 16;
// //
// labelEdgeDistance // labelEdgeDistance
// //
@@ -115,7 +115,7 @@
this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelEdgeDistance.Name = "labelEdgeDistance"; this.labelEdgeDistance.Name = "labelEdgeDistance";
this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13); this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13);
this.labelEdgeDistance.TabIndex = 7; this.labelEdgeDistance.TabIndex = 17;
this.labelEdgeDistance.Text = "Distance From Edge"; this.labelEdgeDistance.Text = "Distance From Edge";
// //
// radioLocCustom // radioLocCustom
@@ -183,7 +183,7 @@
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance"; this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
this.trackBarEdgeDistance.Size = new System.Drawing.Size(148, 30); this.trackBarEdgeDistance.Size = new System.Drawing.Size(148, 30);
this.trackBarEdgeDistance.SmallChange = 2; this.trackBarEdgeDistance.SmallChange = 2;
this.trackBarEdgeDistance.TabIndex = 8; this.trackBarEdgeDistance.TabIndex = 0;
this.trackBarEdgeDistance.TickFrequency = 4; this.trackBarEdgeDistance.TickFrequency = 4;
this.trackBarEdgeDistance.Value = 8; this.trackBarEdgeDistance.Value = 8;
// //
@@ -201,7 +201,7 @@
this.tableLayoutDurationButtons.RowCount = 1; this.tableLayoutDurationButtons.RowCount = 1;
this.tableLayoutDurationButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutDurationButtons.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutDurationButtons.Size = new System.Drawing.Size(171, 27); this.tableLayoutDurationButtons.Size = new System.Drawing.Size(171, 27);
this.tableLayoutDurationButtons.TabIndex = 5; this.tableLayoutDurationButtons.TabIndex = 12;
// //
// btnDurationMedium // btnDurationMedium
// //
@@ -255,7 +255,7 @@
this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelDurationValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelDurationValue.Name = "labelDurationValue"; this.labelDurationValue.Name = "labelDurationValue";
this.labelDurationValue.Size = new System.Drawing.Size(52, 13); this.labelDurationValue.Size = new System.Drawing.Size(52, 13);
this.labelDurationValue.TabIndex = 4; this.labelDurationValue.TabIndex = 1;
this.labelDurationValue.Text = "0 ms/c"; this.labelDurationValue.Text = "0 ms/c";
this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelDurationValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
@@ -267,7 +267,7 @@
this.trackBarDuration.Minimum = 10; this.trackBarDuration.Minimum = 10;
this.trackBarDuration.Name = "trackBarDuration"; this.trackBarDuration.Name = "trackBarDuration";
this.trackBarDuration.Size = new System.Drawing.Size(148, 30); this.trackBarDuration.Size = new System.Drawing.Size(148, 30);
this.trackBarDuration.TabIndex = 3; this.trackBarDuration.TabIndex = 0;
this.trackBarDuration.TickFrequency = 5; this.trackBarDuration.TickFrequency = 5;
this.trackBarDuration.Value = 25; this.trackBarDuration.Value = 25;
// //
@@ -278,7 +278,7 @@
this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkSkipOnLinkClick.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick"; this.checkSkipOnLinkClick.Name = "checkSkipOnLinkClick";
this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17); this.checkSkipOnLinkClick.Size = new System.Drawing.Size(113, 17);
this.checkSkipOnLinkClick.TabIndex = 2; this.checkSkipOnLinkClick.TabIndex = 3;
this.checkSkipOnLinkClick.Text = "Skip On Link Click"; this.checkSkipOnLinkClick.Text = "Skip On Link Click";
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true; this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
// //
@@ -289,7 +289,7 @@
this.checkColumnName.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkColumnName.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkColumnName.Name = "checkColumnName"; this.checkColumnName.Name = "checkColumnName";
this.checkColumnName.Size = new System.Drawing.Size(129, 17); this.checkColumnName.Size = new System.Drawing.Size(129, 17);
this.checkColumnName.TabIndex = 0; this.checkColumnName.TabIndex = 1;
this.checkColumnName.Text = "Display Column Name"; this.checkColumnName.Text = "Display Column Name";
this.checkColumnName.UseVisualStyleBackColor = true; this.checkColumnName.UseVisualStyleBackColor = true;
// //
@@ -300,7 +300,7 @@
this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelIdlePause.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelIdlePause.Name = "labelIdlePause"; this.labelIdlePause.Name = "labelIdlePause";
this.labelIdlePause.Size = new System.Drawing.Size(89, 13); this.labelIdlePause.Size = new System.Drawing.Size(89, 13);
this.labelIdlePause.TabIndex = 4; this.labelIdlePause.TabIndex = 5;
this.labelIdlePause.Text = "Pause When Idle"; this.labelIdlePause.Text = "Pause When Idle";
// //
// comboBoxIdlePause // comboBoxIdlePause
@@ -311,7 +311,7 @@
this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); this.comboBoxIdlePause.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxIdlePause.Name = "comboBoxIdlePause"; this.comboBoxIdlePause.Name = "comboBoxIdlePause";
this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21); this.comboBoxIdlePause.Size = new System.Drawing.Size(144, 21);
this.comboBoxIdlePause.TabIndex = 5; this.comboBoxIdlePause.TabIndex = 6;
// //
// checkNonIntrusive // checkNonIntrusive
// //
@@ -320,7 +320,7 @@
this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkNonIntrusive.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkNonIntrusive.Name = "checkNonIntrusive"; this.checkNonIntrusive.Name = "checkNonIntrusive";
this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17); this.checkNonIntrusive.Size = new System.Drawing.Size(128, 17);
this.checkNonIntrusive.TabIndex = 3; this.checkNonIntrusive.TabIndex = 4;
this.checkNonIntrusive.Text = "Non-Intrusive Popups"; this.checkNonIntrusive.Text = "Non-Intrusive Popups";
this.checkNonIntrusive.UseVisualStyleBackColor = true; this.checkNonIntrusive.UseVisualStyleBackColor = true;
// //
@@ -331,7 +331,7 @@
this.checkTimerCountDown.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkTimerCountDown.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkTimerCountDown.Name = "checkTimerCountDown"; this.checkTimerCountDown.Name = "checkTimerCountDown";
this.checkTimerCountDown.Size = new System.Drawing.Size(119, 17); this.checkTimerCountDown.Size = new System.Drawing.Size(119, 17);
this.checkTimerCountDown.TabIndex = 1; this.checkTimerCountDown.TabIndex = 9;
this.checkTimerCountDown.Text = "Timer Counts Down"; this.checkTimerCountDown.Text = "Timer Counts Down";
this.checkTimerCountDown.UseVisualStyleBackColor = true; this.checkTimerCountDown.UseVisualStyleBackColor = true;
// //
@@ -342,7 +342,7 @@
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkNotificationTimer.Name = "checkNotificationTimer"; this.checkNotificationTimer.Name = "checkNotificationTimer";
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17); this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
this.checkNotificationTimer.TabIndex = 0; this.checkNotificationTimer.TabIndex = 8;
this.checkNotificationTimer.Text = "Display Notification Timer"; this.checkNotificationTimer.Text = "Display Notification Timer";
this.checkNotificationTimer.UseVisualStyleBackColor = true; this.checkNotificationTimer.UseVisualStyleBackColor = true;
// //
@@ -388,7 +388,7 @@
this.panelEdgeDistance.Margin = new System.Windows.Forms.Padding(0); this.panelEdgeDistance.Margin = new System.Windows.Forms.Padding(0);
this.panelEdgeDistance.Name = "panelEdgeDistance"; this.panelEdgeDistance.Name = "panelEdgeDistance";
this.panelEdgeDistance.Size = new System.Drawing.Size(322, 36); this.panelEdgeDistance.Size = new System.Drawing.Size(322, 36);
this.panelEdgeDistance.TabIndex = 1; this.panelEdgeDistance.TabIndex = 18;
// //
// checkMediaPreviews // checkMediaPreviews
// //
@@ -397,7 +397,7 @@
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkMediaPreviews.Name = "checkMediaPreviews"; this.checkMediaPreviews.Name = "checkMediaPreviews";
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17); this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17);
this.checkMediaPreviews.TabIndex = 1; this.checkMediaPreviews.TabIndex = 2;
this.checkMediaPreviews.Text = "Show Media Previews"; this.checkMediaPreviews.Text = "Show Media Previews";
this.checkMediaPreviews.UseVisualStyleBackColor = true; this.checkMediaPreviews.UseVisualStyleBackColor = true;
// //
@@ -407,7 +407,7 @@
this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelScrollSpeedValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelScrollSpeedValue.Name = "labelScrollSpeedValue"; this.labelScrollSpeedValue.Name = "labelScrollSpeedValue";
this.labelScrollSpeedValue.Size = new System.Drawing.Size(38, 13); this.labelScrollSpeedValue.Size = new System.Drawing.Size(38, 13);
this.labelScrollSpeedValue.TabIndex = 4; this.labelScrollSpeedValue.TabIndex = 1;
this.labelScrollSpeedValue.Text = "100%"; this.labelScrollSpeedValue.Text = "100%";
this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelScrollSpeedValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
@@ -421,7 +421,7 @@
this.trackBarScrollSpeed.Name = "trackBarScrollSpeed"; this.trackBarScrollSpeed.Name = "trackBarScrollSpeed";
this.trackBarScrollSpeed.Size = new System.Drawing.Size(148, 30); this.trackBarScrollSpeed.Size = new System.Drawing.Size(148, 30);
this.trackBarScrollSpeed.SmallChange = 5; this.trackBarScrollSpeed.SmallChange = 5;
this.trackBarScrollSpeed.TabIndex = 3; this.trackBarScrollSpeed.TabIndex = 0;
this.trackBarScrollSpeed.TickFrequency = 25; this.trackBarScrollSpeed.TickFrequency = 25;
this.trackBarScrollSpeed.Value = 100; this.trackBarScrollSpeed.Value = 100;
// //
@@ -432,7 +432,7 @@
this.labelScrollSpeed.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelScrollSpeed.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelScrollSpeed.Name = "labelScrollSpeed"; this.labelScrollSpeed.Name = "labelScrollSpeed";
this.labelScrollSpeed.Size = new System.Drawing.Size(67, 13); this.labelScrollSpeed.Size = new System.Drawing.Size(67, 13);
this.labelScrollSpeed.TabIndex = 2; this.labelScrollSpeed.TabIndex = 21;
this.labelScrollSpeed.Text = "Scroll Speed"; this.labelScrollSpeed.Text = "Scroll Speed";
// //
// labelLocation // labelLocation
@@ -443,7 +443,7 @@
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelLocation.Name = "labelLocation"; this.labelLocation.Name = "labelLocation";
this.labelLocation.Size = new System.Drawing.Size(70, 20); this.labelLocation.Size = new System.Drawing.Size(70, 20);
this.labelLocation.TabIndex = 4; this.labelLocation.TabIndex = 13;
this.labelLocation.Text = "Location"; this.labelLocation.Text = "Location";
// //
// panelLocation // panelLocation
@@ -458,7 +458,7 @@
this.panelLocation.Margin = new System.Windows.Forms.Padding(0); this.panelLocation.Margin = new System.Windows.Forms.Padding(0);
this.panelLocation.Name = "panelLocation"; this.panelLocation.Name = "panelLocation";
this.panelLocation.Size = new System.Drawing.Size(322, 49); this.panelLocation.Size = new System.Drawing.Size(322, 49);
this.panelLocation.TabIndex = 5; this.panelLocation.TabIndex = 14;
// //
// panelTimer // panelTimer
// //
@@ -469,7 +469,7 @@
this.panelTimer.Margin = new System.Windows.Forms.Padding(0); this.panelTimer.Margin = new System.Windows.Forms.Padding(0);
this.panelTimer.Name = "panelTimer"; this.panelTimer.Name = "panelTimer";
this.panelTimer.Size = new System.Drawing.Size(322, 36); this.panelTimer.Size = new System.Drawing.Size(322, 36);
this.panelTimer.TabIndex = 3; this.panelTimer.TabIndex = 11;
// //
// labelDuration // labelDuration
// //
@@ -478,7 +478,7 @@
this.labelDuration.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelDuration.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDuration.Name = "labelDuration"; this.labelDuration.Name = "labelDuration";
this.labelDuration.Size = new System.Drawing.Size(47, 13); this.labelDuration.Size = new System.Drawing.Size(47, 13);
this.labelDuration.TabIndex = 2; this.labelDuration.TabIndex = 10;
this.labelDuration.Text = "Duration"; this.labelDuration.Text = "Duration";
// //
// labelTimer // labelTimer
@@ -489,7 +489,7 @@
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelTimer.Name = "labelTimer"; this.labelTimer.Name = "labelTimer";
this.labelTimer.Size = new System.Drawing.Size(48, 20); this.labelTimer.Size = new System.Drawing.Size(48, 20);
this.labelTimer.TabIndex = 2; this.labelTimer.TabIndex = 7;
this.labelTimer.Text = "Timer"; this.labelTimer.Text = "Timer";
// //
// labelSize // labelSize
@@ -500,7 +500,7 @@
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0); this.labelSize.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelSize.Name = "labelSize"; this.labelSize.Name = "labelSize";
this.labelSize.Size = new System.Drawing.Size(40, 20); this.labelSize.Size = new System.Drawing.Size(40, 20);
this.labelSize.TabIndex = 6; this.labelSize.TabIndex = 19;
this.labelSize.Text = "Size"; this.labelSize.Text = "Size";
// //
// panelSize // panelSize
@@ -513,7 +513,7 @@
this.panelSize.Margin = new System.Windows.Forms.Padding(0); this.panelSize.Margin = new System.Windows.Forms.Padding(0);
this.panelSize.Name = "panelSize"; this.panelSize.Name = "panelSize";
this.panelSize.Size = new System.Drawing.Size(322, 25); this.panelSize.Size = new System.Drawing.Size(322, 25);
this.panelSize.TabIndex = 7; this.panelSize.TabIndex = 20;
// //
// durationUpdateTimer // durationUpdateTimer
// //
@@ -552,7 +552,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 678); this.flowPanel.Size = new System.Drawing.Size(322, 678);
this.flowPanel.TabIndex = 8; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// panelScrollSpeed // panelScrollSpeed
@@ -564,7 +564,7 @@
this.panelScrollSpeed.Margin = new System.Windows.Forms.Padding(0); this.panelScrollSpeed.Margin = new System.Windows.Forms.Padding(0);
this.panelScrollSpeed.Name = "panelScrollSpeed"; this.panelScrollSpeed.Name = "panelScrollSpeed";
this.panelScrollSpeed.Size = new System.Drawing.Size(322, 36); this.panelScrollSpeed.Size = new System.Drawing.Size(322, 36);
this.panelScrollSpeed.TabIndex = 9; this.panelScrollSpeed.TabIndex = 22;
// //
// TabSettingsNotifications // TabSettingsNotifications
// //

View File

@@ -170,7 +170,7 @@ namespace TweetDuck.Core.Other.Settings{
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 = TweetNotification.Position.TopRight;
notification.MoveToVisibleLocation(); notification.MoveToVisibleLocation();

View File

@@ -36,6 +36,7 @@
this.trackBarVolume = new System.Windows.Forms.TrackBar(); this.trackBarVolume = new System.Windows.Forms.TrackBar();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.panelVolume = new System.Windows.Forms.Panel(); this.panelVolume = new System.Windows.Forms.Panel();
this.volumeUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.panelSoundNotification.SuspendLayout(); this.panelSoundNotification.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
this.flowPanel.SuspendLayout(); this.flowPanel.SuspendLayout();
@@ -58,7 +59,7 @@
this.labelVolumeValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); this.labelVolumeValue.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.labelVolumeValue.Name = "labelVolumeValue"; this.labelVolumeValue.Name = "labelVolumeValue";
this.labelVolumeValue.Size = new System.Drawing.Size(38, 13); this.labelVolumeValue.Size = new System.Drawing.Size(38, 13);
this.labelVolumeValue.TabIndex = 6; this.labelVolumeValue.TabIndex = 1;
this.labelVolumeValue.Text = "100%"; this.labelVolumeValue.Text = "100%";
this.labelVolumeValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelVolumeValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
// //
@@ -105,7 +106,7 @@
this.labelSoundNotification.Margin = new System.Windows.Forms.Padding(0); this.labelSoundNotification.Margin = new System.Windows.Forms.Padding(0);
this.labelSoundNotification.Name = "labelSoundNotification"; this.labelSoundNotification.Name = "labelSoundNotification";
this.labelSoundNotification.Size = new System.Drawing.Size(198, 20); this.labelSoundNotification.Size = new System.Drawing.Size(198, 20);
this.labelSoundNotification.TabIndex = 1; this.labelSoundNotification.TabIndex = 0;
this.labelSoundNotification.Text = "Custom Sound Notification"; this.labelSoundNotification.Text = "Custom Sound Notification";
// //
// panelSoundNotification // panelSoundNotification
@@ -119,7 +120,7 @@
this.panelSoundNotification.Margin = new System.Windows.Forms.Padding(0); this.panelSoundNotification.Margin = new System.Windows.Forms.Padding(0);
this.panelSoundNotification.Name = "panelSoundNotification"; this.panelSoundNotification.Name = "panelSoundNotification";
this.panelSoundNotification.Size = new System.Drawing.Size(322, 55); this.panelSoundNotification.Size = new System.Drawing.Size(322, 55);
this.panelSoundNotification.TabIndex = 2; this.panelSoundNotification.TabIndex = 1;
// //
// labelVolume // labelVolume
// //
@@ -128,7 +129,7 @@
this.labelVolume.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelVolume.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelVolume.Name = "labelVolume"; this.labelVolume.Name = "labelVolume";
this.labelVolume.Size = new System.Drawing.Size(42, 13); this.labelVolume.Size = new System.Drawing.Size(42, 13);
this.labelVolume.TabIndex = 4; this.labelVolume.TabIndex = 2;
this.labelVolume.Text = "Volume"; this.labelVolume.Text = "Volume";
// //
// trackBarVolume // trackBarVolume
@@ -139,7 +140,7 @@
this.trackBarVolume.Maximum = 100; this.trackBarVolume.Maximum = 100;
this.trackBarVolume.Name = "trackBarVolume"; this.trackBarVolume.Name = "trackBarVolume";
this.trackBarVolume.Size = new System.Drawing.Size(148, 30); this.trackBarVolume.Size = new System.Drawing.Size(148, 30);
this.trackBarVolume.TabIndex = 5; this.trackBarVolume.TabIndex = 0;
this.trackBarVolume.TickFrequency = 10; this.trackBarVolume.TickFrequency = 10;
this.trackBarVolume.Value = 100; this.trackBarVolume.Value = 100;
this.trackBarVolume.ValueChanged += new System.EventHandler(this.trackBarVolume_ValueChanged); this.trackBarVolume.ValueChanged += new System.EventHandler(this.trackBarVolume_ValueChanged);
@@ -157,7 +158,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 136); this.flowPanel.Size = new System.Drawing.Size(322, 136);
this.flowPanel.TabIndex = 3; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// panelVolume // panelVolume
@@ -168,7 +169,12 @@
this.panelVolume.Margin = new System.Windows.Forms.Padding(0); this.panelVolume.Margin = new System.Windows.Forms.Padding(0);
this.panelVolume.Name = "panelVolume"; this.panelVolume.Name = "panelVolume";
this.panelVolume.Size = new System.Drawing.Size(322, 36); this.panelVolume.Size = new System.Drawing.Size(322, 36);
this.panelVolume.TabIndex = 2; this.panelVolume.TabIndex = 3;
//
// volumeUpdateTimer
//
this.volumeUpdateTimer.Interval = 250;
this.volumeUpdateTimer.Tick += new System.EventHandler(this.volumeUpdateTimer_Tick);
// //
// TabSettingsSounds // TabSettingsSounds
// //
@@ -201,5 +207,6 @@
private System.Windows.Forms.TrackBar trackBarVolume; private System.Windows.Forms.TrackBar trackBarVolume;
private System.Windows.Forms.FlowLayoutPanel flowPanel; private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.Panel panelVolume; private System.Windows.Forms.Panel panelVolume;
private System.Windows.Forms.Timer volumeUpdateTimer;
} }
} }

View File

@@ -4,25 +4,18 @@ using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetLib.Audio;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsSounds : BaseTabSettings{ sealed partial class TabSettingsSounds : BaseTabSettings{
private readonly SoundNotification soundNotification; private readonly Action playSoundNotification;
private readonly bool supportsChangingVolume;
public TabSettingsSounds(){ public TabSettingsSounds(Action playSoundNotification){
InitializeComponent(); InitializeComponent();
soundNotification = new SoundNotification(); this.playSoundNotification = playSoundNotification;
soundNotification.PlaybackError += sound_PlaybackError;
Disposed += (sender, args) => soundNotification.Dispose();
supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
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.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume); trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
labelVolumeValue.Text = trackBarVolume.Value+"%"; labelVolumeValue.Text = trackBarVolume.Value+"%";
@@ -39,22 +32,29 @@ namespace TweetDuck.Core.Other.Settings{
public override void OnClosing(){ public override void OnClosing(){
Config.NotificationSoundPath = tbCustomSound.Text; Config.NotificationSoundPath = tbCustomSound.Text;
Config.NotificationSoundVolume = trackBarVolume.Value;
}
private bool RefreshCanPlay(){
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text);
bool canPlay = isEmpty || File.Exists(tbCustomSound.Text);
tbCustomSound.ForeColor = canPlay ? SystemColors.WindowText : Color.Red;
btnPlaySound.Enabled = canPlay;
btnResetSound.Enabled = !isEmpty;
return canPlay;
} }
private void tbCustomSound_TextChanged(object sender, EventArgs e){ private void tbCustomSound_TextChanged(object sender, EventArgs e){
bool isEmpty = string.IsNullOrEmpty(tbCustomSound.Text); RefreshCanPlay();
tbCustomSound.ForeColor = isEmpty || File.Exists(tbCustomSound.Text) ? SystemColors.WindowText : Color.Red;
btnPlaySound.Enabled = !isEmpty;
btnResetSound.Enabled = !isEmpty;
trackBarVolume.Enabled = supportsChangingVolume && !isEmpty;
} }
private void btnPlaySound_Click(object sender, EventArgs e){ private void btnPlaySound_Click(object sender, EventArgs e){
soundNotification.Play(tbCustomSound.Text); if (RefreshCanPlay()){
Config.NotificationSoundPath = tbCustomSound.Text;
Config.NotificationSoundVolume = trackBarVolume.Value;
playSoundNotification();
} }
private void sound_PlaybackError(object sender, PlaybackErrorEventArgs e){
FormMessage.Error("Notification Sound Error", "Could not play custom notification sound.\n"+e.Message, FormMessage.OK);
} }
private void btnBrowseSound_Click(object sender, EventArgs e){ private void btnBrowseSound_Click(object sender, EventArgs e){
@@ -62,7 +62,7 @@ namespace TweetDuck.Core.Other.Settings{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
DereferenceLinks = true, DereferenceLinks = true,
Title = "Custom Notification Sound", Title = "Custom Notification Sound",
Filter = "Sound file ("+soundNotification.SupportedFormats+")|"+soundNotification.SupportedFormats+"|All files (*.*)|*.*" Filter = $"Sound file ({SoundNotification.SupportedFormats})|{SoundNotification.SupportedFormats}|All files (*.*)|*.*"
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
tbCustomSound.Text = dialog.FileName; tbCustomSound.Text = dialog.FileName;
@@ -75,9 +75,14 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void trackBarVolume_ValueChanged(object sender, EventArgs e){ private void trackBarVolume_ValueChanged(object sender, EventArgs e){
volumeUpdateTimer.Stop();
volumeUpdateTimer.Start();
labelVolumeValue.Text = trackBarVolume.Value+"%";
}
private void volumeUpdateTimer_Tick(object sender, EventArgs e){
Config.NotificationSoundVolume = trackBarVolume.Value; Config.NotificationSoundVolume = trackBarVolume.Value;
soundNotification.SetVolume(Config.NotificationSoundVolume); volumeUpdateTimer.Stop();
labelVolumeValue.Text = Config.NotificationSoundVolume+"%";
} }
} }
} }

View File

@@ -40,7 +40,7 @@
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3); this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkTrayHighlight.Name = "checkTrayHighlight"; this.checkTrayHighlight.Name = "checkTrayHighlight";
this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17); this.checkTrayHighlight.Size = new System.Drawing.Size(103, 17);
this.checkTrayHighlight.TabIndex = 2; this.checkTrayHighlight.TabIndex = 3;
this.checkTrayHighlight.Text = "Enable Highlight"; this.checkTrayHighlight.Text = "Enable Highlight";
this.checkTrayHighlight.UseVisualStyleBackColor = true; this.checkTrayHighlight.UseVisualStyleBackColor = true;
// //
@@ -52,7 +52,7 @@
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3); this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(5, 5, 3, 3);
this.comboBoxTrayType.Name = "comboBoxTrayType"; this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21); this.comboBoxTrayType.Size = new System.Drawing.Size(144, 21);
this.comboBoxTrayType.TabIndex = 0; this.comboBoxTrayType.TabIndex = 1;
// //
// labelTrayIcon // labelTrayIcon
// //
@@ -61,7 +61,7 @@
this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0); this.labelTrayIcon.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelTrayIcon.Name = "labelTrayIcon"; this.labelTrayIcon.Name = "labelTrayIcon";
this.labelTrayIcon.Size = new System.Drawing.Size(52, 13); this.labelTrayIcon.Size = new System.Drawing.Size(52, 13);
this.labelTrayIcon.TabIndex = 1; this.labelTrayIcon.TabIndex = 2;
this.labelTrayIcon.Text = "Tray Icon"; this.labelTrayIcon.Text = "Tray Icon";
// //
// labelTray // labelTray
@@ -88,7 +88,7 @@
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";
this.flowPanel.Size = new System.Drawing.Size(322, 97); this.flowPanel.Size = new System.Drawing.Size(322, 97);
this.flowPanel.TabIndex = 2; this.flowPanel.TabIndex = 0;
this.flowPanel.WrapContents = false; this.flowPanel.WrapContents = false;
// //
// TabSettingsTray // TabSettingsTray

View File

@@ -7,15 +7,12 @@ 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.Notification;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources; using TweetDuck.Resources;
using TweetDuck.Updates;
namespace TweetDuck.Core{ namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{ sealed class TweetDeckBrowser : ITweetDeckBrowser, IDisposable{
public bool Ready { get; private set; } public bool Ready { get; private set; }
public bool Enabled{ public bool Enabled{
@@ -31,12 +28,11 @@ namespace TweetDuck.Core{
} }
} }
public event EventHandler PageLoaded;
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
private readonly PluginManager plugins;
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge bridge){ private string prevSoundNotificationPath = null;
public TweetDeckBrowser(FormBrowser owner, TweetDeckBridge bridge){
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){ this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
DialogHandler = new FileDialogHandler(), DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(), DragHandler = new DragHandlerBrowser(),
@@ -57,19 +53,19 @@ namespace TweetDuck.Core{
this.browser.LoadError += browser_LoadError; this.browser.LoadError += browser_LoadError;
this.browser.RegisterAsyncJsObject("$TD", bridge); this.browser.RegisterAsyncJsObject("$TD", bridge);
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb(); this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
this.browser.Dock = DockStyle.None; this.browser.Dock = DockStyle.None;
this.browser.Location = ControlExtensions.InvisibleLocation; this.browser.Location = ControlExtensions.InvisibleLocation;
owner.Controls.Add(browser); this.browser.SetupResourceHandler(TweetNotification.AppLogo);
this.browser.SetupResourceHandler(TwitterUtils.LoadingSpinner);
this.plugins = plugins; owner.Controls.Add(browser);
this.plugins.PluginChangedState += plugins_PluginChangedState;
Program.UserConfig.MuteToggled += UserConfig_MuteToggled; Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged; Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
Program.UserConfig.SoundNotificationChanged += UserConfig_SoundNotificationInfoChanged;
} }
// setup and management // setup and management
@@ -87,14 +83,31 @@ namespace TweetDuck.Core{
} }
public void Dispose(){ public void Dispose(){
plugins.PluginChangedState -= plugins_PluginChangedState;
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled; Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged; Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
Program.UserConfig.SoundNotificationChanged -= UserConfig_SoundNotificationInfoChanged;
browser.Dispose(); browser.Dispose();
} }
void ITweetDeckBrowser.RegisterBridge(string name, object obj){
browser.RegisterAsyncJsObject(name, obj);
}
void ITweetDeckBrowser.OnFrameLoaded(Action<IFrame> callback){
browser.FrameLoadEnd += (sender, args) => {
IFrame frame = args.Frame;
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
callback(frame);
}
};
}
void ITweetDeckBrowser.ExecuteFunction(string name, params object[] args){
browser.ExecuteScriptAsync(name, args);
}
// event handlers // event handlers
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
@@ -121,23 +134,23 @@ namespace TweetDuck.Core{
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){ IFrame frame = e.Frame;
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
if (frame.IsMain && TwitterUtils.IsTweetDeckWebsite(frame)){
frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorOverride);
UpdateProperties(); UpdateProperties();
TweetDeckBridge.RestoreSessionData(e.Frame); TweetDeckBridge.RestoreSessionData(frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js"); ScriptLoader.ExecuteFile(frame, "code.js");
InjectBrowserCSS(); InjectBrowserCSS();
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS); ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser); UserConfig_SoundNotificationInfoChanged(null, EventArgs.Empty);
TweetDeckBridge.ResetStaticProperties(); TweetDeckBridge.ResetStaticProperties();
if (Program.UserConfig.FirstRun){ if (Program.UserConfig.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js"); ScriptLoader.ExecuteFile(frame, "introduction.js");
} }
PageLoaded?.Invoke(this, EventArgs.Empty);
} }
} }
@@ -155,10 +168,6 @@ namespace TweetDuck.Core{
} }
} }
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
private void UserConfig_MuteToggled(object sender, EventArgs e){ private void UserConfig_MuteToggled(object sender, EventArgs e){
UpdateProperties(); UpdateProperties();
} }
@@ -167,12 +176,20 @@ namespace TweetDuck.Core{
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel); BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
} }
// external handling private void UserConfig_SoundNotificationInfoChanged(object sender, EventArgs e){
const string soundUrl = "https://ton.twimg.com/tduck/updatesnd";
bool hasCustomSound = Program.UserConfig.IsCustomSoundNotificationSet;
public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){ if (prevSoundNotificationPath != Program.UserConfig.NotificationSoundPath){
return new UpdateHandler(browser, settings); browser.SetupResourceHandler(soundUrl, hasCustomSound ? SoundNotification.CreateFileHandler(Program.UserConfig.NotificationSoundPath) : null);
prevSoundNotificationPath = Program.UserConfig.NotificationSoundPath;
} }
browser.ExecuteScriptAsync("TDGF_setSoundNotificationData", hasCustomSound, Program.UserConfig.NotificationSoundVolume);
}
// external handling
public void HideVideoOverlay(bool focus){ public void HideVideoOverlay(bool focus){
if (focus){ if (focus){
browser.GetBrowser().GetHost().SendFocusEvent(true); browser.GetBrowser().GetHost().SendFocusEvent(true);
@@ -215,6 +232,10 @@ namespace TweetDuck.Core{
browser.ExecuteScriptAsync("TDGF_reloadColumns()"); browser.ExecuteScriptAsync("TDGF_reloadColumns()");
} }
public void PlaySoundNotification(){
browser.ExecuteScriptAsync("TDGF_playSoundNotification()");
}
public void ApplyROT13(){ public void ApplyROT13(){
browser.ExecuteScriptAsync("TDGF_applyROT13()"); browser.ExecuteScriptAsync("TDGF_applyROT13()");
} }

View File

@@ -7,10 +7,10 @@ using System.Net;
using System.Windows.Forms; using System.Windows.Forms;
using CefSharp.WinForms; using CefSharp.WinForms;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Data;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public static string HeaderAcceptLanguage => "en-us,en";
public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion; public static string HeaderUserAgent => Program.BrandName+" "+Application.ProductVersion;
public static void SetupCefArgs(IDictionary<string, string> args){ public static void SetupCefArgs(IDictionary<string, string> args){
@@ -19,6 +19,10 @@ namespace TweetDuck.Core.Utils{
args["disable-gpu-vsync"] = "1"; args["disable-gpu-vsync"] = "1";
} }
if (!Program.UserConfig.EnableSmoothScrolling){
args["disable-smooth-scrolling"] = "1";
}
args["disable-pdf-extension"] = "1"; args["disable-pdf-extension"] = "1";
args["disable-plugins-discovery"] = "1"; args["disable-plugins-discovery"] = "1";
args["enable-system-flash"] = "0"; args["enable-system-flash"] = "0";
@@ -35,6 +39,21 @@ namespace TweetDuck.Core.Utils{
return (ChromiumWebBrowser)browserControl; return (ChromiumWebBrowser)browserControl;
} }
public static void SetupResourceHandler(this ChromiumWebBrowser browser, string url, IResourceHandler handler){
DefaultResourceHandlerFactory factory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
if (handler == null){
factory.UnregisterHandler(url);
}
else{
factory.RegisterHandler(url, handler);
}
}
public static void SetupResourceHandler(this ChromiumWebBrowser browser, ResourceLink resource){
browser.SetupResourceHandler(resource.Url, resource.Handler);
}
private const string TwitterTrackingUrl = "t.co"; private const string TwitterTrackingUrl = "t.co";
public enum UrlCheckResult{ public enum UrlCheckResult{
@@ -62,14 +81,42 @@ namespace TweetDuck.Core.Utils{
FormGuide.Show(hash); FormGuide.Show(hash);
} }
else{ else{
string browserPath = Program.UserConfig.BrowserPath;
if (browserPath == null || !File.Exists(browserPath)){
WindowsUtils.OpenAssociatedProgram(url); WindowsUtils.OpenAssociatedProgram(url);
} }
else{
try{
using(Process.Start(browserPath, url)){}
}catch(Exception e){
Program.Reporter.HandleException("Error Opening Browser", "Could not open the browser.", true, e);
}
}
}
break; break;
case UrlCheckResult.Tracking: case UrlCheckResult.Tracking:
if (FormMessage.Warning("Blocked URL", "TweetDuck has blocked a tracking url due to privacy concerns. Do you want to visit it anyway?\n"+url, FormMessage.Yes, FormMessage.No)){ if (Program.UserConfig.IgnoreTrackingUrlWarning){
WindowsUtils.OpenAssociatedProgram(url); goto case UrlCheckResult.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)){
form.AddButton(FormMessage.No, DialogResult.No, ControlType.Cancel | ControlType.Focused);
form.AddButton(FormMessage.Yes, DialogResult.Yes, ControlType.Accept);
form.AddButton("Always Visit", DialogResult.Ignore);
DialogResult result = form.ShowDialog();
if (result == DialogResult.Ignore){
Program.UserConfig.IgnoreTrackingUrlWarning = true;
Program.UserConfig.Save();
}
if (result == DialogResult.Ignore || result == DialogResult.Yes){
goto case UrlCheckResult.Fine;
}
} }
break; break;

View File

@@ -6,13 +6,16 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Data;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class TwitterUtils{ static class TwitterUtils{
public const string TweetDeckURL = "https://tweetdeck.twitter.com"; public const string TweetDeckURL = "https://tweetdeck.twitter.com";
public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153); public static readonly Color BackgroundColor = Color.FromArgb(28, 99, 153);
public const string BackgroundColorFix = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body::before{background:#1c6399!important}'"; public const string BackgroundColorOverride = "let e=document.createElement('style');document.head.appendChild(e);e.innerHTML='body,body::before{background:#1c6399!important}'";
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$)([^/]+)/?$", RegexOptions.Compiled), false); private static readonly Lazy<Regex> RegexAccountLazy = new Lazy<Regex>(() => new Regex(@"^https?://twitter\.com/(?!signup$|tos$|privacy$)([^/]+)/?$", RegexOptions.Compiled), false);
public static Regex RegexAccount => RegexAccountLazy.Value; public static Regex RegexAccount => RegexAccountLazy.Value;
@@ -87,7 +90,7 @@ namespace TweetDuck.Core.Utils{
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 = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{ext}", FileName = $"{string.Join(" ", fileNameParts.Where(part => part.Length > 0))}{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}")
}){ }){
@@ -118,7 +121,7 @@ namespace TweetDuck.Core.Utils{
using(SaveFileDialog dialog = new SaveFileDialog{ using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
OverwritePrompt = true, OverwritePrompt = true,
Title = "Save video", Title = "Save Video",
FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}", FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}",
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){ }){

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@@ -6,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Win32;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class WindowsUtils{ static class WindowsUtils{
@@ -62,7 +64,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 file", e.Message, true, e); Program.Reporter.HandleException("Error Opening Program", "Could not open the associated program for "+file, true, e);
return false; return false;
} }
} }
@@ -130,5 +132,63 @@ namespace TweetDuck.Core.Utils{
Program.Reporter.HandleException("Clipboard Error", "TweetDuck could not access the clipboard as it is currently used by another process.", true, e); Program.Reporter.HandleException("Clipboard Error", "TweetDuck could not access the clipboard as it is currently used by another process.", true, e);
} }
} }
public static IEnumerable<Browser> FindInstalledBrowsers(){
IEnumerable<Browser> ReadBrowsersFromKey(RegistryHive hive){
using(RegistryKey root = RegistryKey.OpenBaseKey(hive, RegistryView.Default))
using(RegistryKey browserList = root.OpenSubKey(@"SOFTWARE\Clients\StartMenuInternet", false)){
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;
}
string browserName = browserKey.GetValue(null) as string;
string browserPath = shellKey.GetValue(null) as string;
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);
}
}
}
}
HashSet<Browser> browsers = new HashSet<Browser>();
try{
browsers.UnionWith(ReadBrowsersFromKey(RegistryHive.CurrentUser));
browsers.UnionWith(ReadBrowsersFromKey(RegistryHive.LocalMachine));
}catch{
// oops I guess
}
return browsers;
}
public sealed class Browser{
public string Name { get; }
public string Path { get; }
public Browser(string name, string path){
this.Name = name;
this.Path = path;
}
public override int GetHashCode() => Name.GetHashCode();
public override bool Equals(object obj) => obj is Browser other && Name == other.Name;
public override string ToString() => Name;
}
} }
} }

13
Data/ResourceLink.cs Normal file
View File

@@ -0,0 +1,13 @@
using CefSharp;
namespace TweetDuck.Data{
sealed class ResourceLink{
public string Url { get; }
public IResourceHandler Handler { get; }
public ResourceLink(string url, IResourceHandler handler){
this.Url = url;
this.Handler = handler;
}
}
}

View File

@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Data.Serialization{ namespace TweetDuck.Data.Serialization{
@@ -11,10 +12,45 @@ namespace TweetDuck.Data.Serialization{
private const string NewLineReal = "\r\n"; private const string NewLineReal = "\r\n";
private const string NewLineCustom = "\r~\n"; private const string NewLineCustom = "\r~\n";
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter(); private static string EscapeLine(string input) => input.Replace("\\", "\\\\").Replace(Environment.NewLine, "\\\r\n");
private static string UnescapeLine(string input) => input.Replace(NewLineCustom, Environment.NewLine);
public delegate void HandleUnknownPropertiesHandler(T obj, Dictionary<string, string> data); private static string UnescapeStream(StreamReader reader){
public HandleUnknownPropertiesHandler HandleUnknownProperties { get; set; } string data = reader.ReadToEnd();
StringBuilder build = new StringBuilder(data.Length);
int index = 0;
while(true){
int nextIndex = data.IndexOf('\\', index);
if (nextIndex == -1 || nextIndex+1 >= data.Length){
break;
}
else{
build.Append(data.Substring(index, nextIndex-index));
char next = data[nextIndex+1];
if (next == '\\'){ // convert double backslash to single backslash
build.Append('\\');
index = nextIndex+2;
}
else if (next == '\r' && nextIndex+2 < data.Length && data[nextIndex+2] == '\n'){ // convert backslash followed by CRLF to custom new line
build.Append(NewLineCustom);
index = nextIndex+3;
}
else{ // single backslash
build.Append('\\');
index = nextIndex+1;
}
}
}
return build.Append(data.Substring(index)).ToString();
}
private static readonly ITypeConverter BasicSerializerObj = new BasicTypeConverter();
private readonly Dictionary<string, PropertyInfo> props; private readonly Dictionary<string, PropertyInfo> props;
private readonly Dictionary<Type, ITypeConverter> converters; private readonly Dictionary<Type, ITypeConverter> converters;
@@ -42,7 +78,7 @@ namespace TweetDuck.Data.Serialization{
if (serializer.TryWriteType(type, value, out string converted)){ if (serializer.TryWriteType(type, value, out string converted)){
if (converted != null){ if (converted != null){
writer.Write($"{prop.Key} {converted.Replace(Environment.NewLine, NewLineCustom)}"); writer.Write($"{prop.Key} {EscapeLine(converted)}");
writer.Write(NewLineReal); writer.Write(NewLineReal);
} }
} }
@@ -54,18 +90,38 @@ namespace TweetDuck.Data.Serialization{
} }
public void Read(string file, T obj){ public void Read(string file, T obj){
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4); string contents;
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){ using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))){
switch(reader.Peek()){ contents = UnescapeStream(reader);
case -1: }
if (string.IsNullOrWhiteSpace(contents)){
throw new FormatException("File is empty."); throw new FormatException("File is empty.");
case 0: }
case 1: else if (contents[0] <= (char)1){
throw new FormatException("Input appears to be a binary file."); throw new FormatException("Input appears to be a binary file.");
} }
foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){ int currentPos = 0;
do{
string line;
int nextPos = contents.IndexOf(NewLineReal, currentPos);
if (nextPos == -1){
line = contents.Substring(currentPos);
currentPos = -1;
if (string.IsNullOrEmpty(line)){
break;
}
}
else{
line = contents.Substring(currentPos, nextPos-currentPos);
currentPos = nextPos+NewLineReal.Length;
}
int space = line.IndexOf(' '); int space = line.IndexOf(' ');
if (space == -1){ if (space == -1){
@@ -73,7 +129,7 @@ namespace TweetDuck.Data.Serialization{
} }
string property = line.Substring(0, space); string property = line.Substring(0, space);
string value = line.Substring(space+1).Replace(NewLineCustom, Environment.NewLine); string value = UnescapeLine(line.Substring(space+1));
if (props.TryGetValue(property, out PropertyInfo info)){ if (props.TryGetValue(property, out PropertyInfo info)){
if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)){ if (!converters.TryGetValue(info.PropertyType, out ITypeConverter serializer)){
@@ -87,19 +143,7 @@ namespace TweetDuck.Data.Serialization{
throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})"); throw new SerializationException($"Invalid file format, cannot convert value: {value} (property: {property})");
} }
} }
else{ }while(currentPos != -1);
unknownProperties[property] = value;
}
}
}
if (unknownProperties.Count > 0){
HandleUnknownProperties?.Invoke(obj, unknownProperties);
if (unknownProperties.Count > 0){
throw new SerializationException($"Invalid file format, unknown properties: {string.Join(", ", unknownProperties.Keys)}");
}
}
} }
public void ReadIfExists(string file, T obj){ public void ReadIfExists(string file, T obj){
@@ -109,14 +153,6 @@ namespace TweetDuck.Data.Serialization{
}catch(DirectoryNotFoundException){} }catch(DirectoryNotFoundException){}
} }
public static HandleUnknownPropertiesHandler IgnoreProperties(params string[] properties){
return (obj, data) => {
foreach(string property in properties){
data.Remove(property);
}
};
}
private sealed class BasicTypeConverter : ITypeConverter{ private sealed class BasicTypeConverter : ITypeConverter{
bool ITypeConverter.TryWriteType(Type type, object value, out string converted){ bool ITypeConverter.TryWriteType(Type type, object value, out string converted){
switch(Type.GetTypeCode(type)){ switch(Type.GetTypeCode(type)){

View File

@@ -31,7 +31,7 @@
this.flowLayoutInfo = new System.Windows.Forms.FlowLayoutPanel(); this.flowLayoutInfo = new System.Windows.Forms.FlowLayoutPanel();
this.labelWebsite = new System.Windows.Forms.Label(); this.labelWebsite = new System.Windows.Forms.Label();
this.labelVersion = new System.Windows.Forms.Label(); this.labelVersion = new System.Windows.Forms.Label();
this.btnOpenConfig = new System.Windows.Forms.Button(); this.btnConfigure = new System.Windows.Forms.Button();
this.labelType = new TweetDuck.Core.Controls.LabelVertical(); this.labelType = new TweetDuck.Core.Controls.LabelVertical();
this.panelDescription.SuspendLayout(); this.panelDescription.SuspendLayout();
this.flowLayoutInfo.SuspendLayout(); this.flowLayoutInfo.SuspendLayout();
@@ -135,16 +135,16 @@
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.labelVersion.UseMnemonic = false; this.labelVersion.UseMnemonic = false;
// //
// btnOpenConfig // btnConfigure
// //
this.btnOpenConfig.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnConfigure.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOpenConfig.Location = new System.Drawing.Point(382, 80); this.btnConfigure.Location = new System.Drawing.Point(382, 80);
this.btnOpenConfig.Name = "btnOpenConfig"; this.btnConfigure.Name = "btnConfigure";
this.btnOpenConfig.Size = new System.Drawing.Size(68, 23); this.btnConfigure.Size = new System.Drawing.Size(68, 23);
this.btnOpenConfig.TabIndex = 4; this.btnConfigure.TabIndex = 4;
this.btnOpenConfig.Text = "Configure"; this.btnConfigure.Text = "Configure";
this.btnOpenConfig.UseVisualStyleBackColor = true; this.btnConfigure.UseVisualStyleBackColor = true;
this.btnOpenConfig.Click += new System.EventHandler(this.btnOpenConfig_Click); this.btnConfigure.Click += new System.EventHandler(this.btnConfigure_Click);
// //
// labelType // labelType
// //
@@ -163,7 +163,7 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.labelType); this.Controls.Add(this.labelType);
this.Controls.Add(this.btnOpenConfig); this.Controls.Add(this.btnConfigure);
this.Controls.Add(this.flowLayoutInfo); this.Controls.Add(this.flowLayoutInfo);
this.Controls.Add(this.panelDescription); this.Controls.Add(this.panelDescription);
this.Controls.Add(this.labelName); this.Controls.Add(this.labelName);
@@ -194,7 +194,7 @@
private System.Windows.Forms.FlowLayoutPanel flowLayoutInfo; private System.Windows.Forms.FlowLayoutPanel flowLayoutInfo;
private System.Windows.Forms.Label labelWebsite; private System.Windows.Forms.Label labelWebsite;
private System.Windows.Forms.Label labelVersion; private System.Windows.Forms.Label labelVersion;
private System.Windows.Forms.Button btnOpenConfig; private System.Windows.Forms.Button btnConfigure;
private Core.Controls.LabelVertical labelType; private Core.Controls.LabelVertical labelType;
} }
} }

View File

@@ -1,7 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -57,8 +55,9 @@ namespace TweetDuck.Plugins.Controls{
} }
} }
private void btnOpenConfig_Click(object sender, EventArgs e){ private void btnConfigure_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe", "/select,\""+plugin.ConfigPath.Replace('/', '\\')+"\"")){} pluginManager.ConfigurePlugin(plugin);
ParentForm?.Close();
} }
private void btnToggleState_Click(object sender, EventArgs e){ private void btnToggleState_Click(object sender, EventArgs e){
@@ -87,14 +86,13 @@ namespace TweetDuck.Plugins.Controls{
labelName.ForeColor = textColor; labelName.ForeColor = textColor;
labelDescription.ForeColor = textColor; labelDescription.ForeColor = textColor;
btnToggleState.Text = isEnabled ? "Disable" : "Enable"; btnToggleState.Text = isEnabled ? "Disable" : "Enable";
btnOpenConfig.Visible = plugin.HasConfig; btnConfigure.Visible = isEnabled && pluginManager.IsPluginConfigurable(plugin);
btnOpenConfig.Enabled = btnOpenConfig.Visible && File.Exists(plugin.ConfigPath);
} }
else{ else{
labelName.ForeColor = Color.DarkRed; labelName.ForeColor = Color.DarkRed;
labelDescription.ForeColor = Color.DarkRed; labelDescription.ForeColor = Color.DarkRed;
btnToggleState.Visible = false; btnToggleState.Visible = false;
btnOpenConfig.Visible = false; btnConfigure.Visible = false;
} }
} }
} }

View File

@@ -11,5 +11,13 @@
default: return "unknown/"; default: return "unknown/";
} }
} }
public static string GetIdentifierPrefixShort(this PluginGroup group){
switch(group){
case PluginGroup.Official: return "o/";
case PluginGroup.Custom: return "c/";
default: return "?/";
}
}
} }
} }

View File

@@ -18,11 +18,12 @@ namespace TweetDuck.Plugins{
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; public IEnumerable<InjectedHTML> NotificationInjections => notificationInjections.InnerValues;
public HashSet<Plugin> WithConfigureFunction { get; } = new HashSet<Plugin>();
public PluginBridge(PluginManager manager){ public PluginBridge(PluginManager manager){
this.manager = manager; this.manager = manager;
this.manager.Reloaded += manager_Reloaded; this.manager.Reloaded += manager_Reloaded;
this.manager.PluginChangedState += manager_PluginChangedState; this.manager.Config.PluginChangedState += Config_PluginChangedState;
} }
// Event handlers // Event handlers
@@ -31,7 +32,7 @@ namespace TweetDuck.Plugins{
fileCache.Clear(); fileCache.Clear();
} }
private void manager_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 = manager.GetTokenFromPlugin(e.Plugin);
@@ -114,5 +115,13 @@ namespace TweetDuck.Plugins{
public void InjectIntoNotificationsAfter(int token, string key, string search, string html){ public void InjectIntoNotificationsAfter(int token, string key, string search, string html){
notificationInjections[token, key] = new InjectedHTML(InjectedHTML.Position.After, search, html); notificationInjections[token, key] = new InjectedHTML(InjectedHTML.Position.After, search, html);
} }
public void SetConfigurable(int token){
Plugin plugin = manager.GetPluginFromToken(token);
if (plugin != null){
WithConfigureFunction.Add(plugin);
}
}
} }
} }

View File

@@ -6,7 +6,7 @@ using TweetDuck.Plugins.Events;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
sealed class PluginConfig{ sealed class PluginConfig{
public event EventHandler<PluginChangedStateEventArgs> InternalPluginChangedState; // should only be accessed from PluginManager public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
public IEnumerable<string> DisabledPlugins => disabled; public IEnumerable<string> DisabledPlugins => disabled;
public bool AnyDisabled => disabled.Count > 0; public bool AnyDisabled => disabled.Count > 0;
@@ -20,7 +20,7 @@ namespace TweetDuck.Plugins{
public void SetEnabled(Plugin plugin, bool enabled){ public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){ if ((enabled && disabled.Remove(plugin.Identifier)) || (!enabled && disabled.Add(plugin.Identifier))){
InternalPluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled)); PluginChangedState?.Invoke(this, new PluginChangedStateEventArgs(plugin, enabled));
} }
} }

View File

@@ -1,8 +1,10 @@
using CefSharp; using CefSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using TweetDuck.Core;
using TweetDuck.Plugins.Enums; using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
using TweetDuck.Resources; using TweetDuck.Resources;
@@ -24,8 +26,8 @@ namespace TweetDuck.Plugins{
public event EventHandler<PluginErrorEventArgs> Reloaded; public event EventHandler<PluginErrorEventArgs> Reloaded;
public event EventHandler<PluginErrorEventArgs> Executed; public event EventHandler<PluginErrorEventArgs> Executed;
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
private readonly ITweetDeckBrowser browser;
private readonly string rootPath; private readonly string rootPath;
private readonly string configPath; private readonly string configPath;
@@ -33,22 +35,30 @@ namespace TweetDuck.Plugins{
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>(); private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
private readonly Random rand = new Random(); private readonly Random rand = new Random();
public PluginManager(string rootPath, string configPath){ public PluginManager(ITweetDeckBrowser browser, string rootPath, string configPath){
this.browser = browser;
this.rootPath = rootPath; this.rootPath = rootPath;
this.configPath = configPath; this.configPath = configPath;
this.Config = new PluginConfig(); this.Config = new PluginConfig();
this.Bridge = new PluginBridge(this); this.Bridge = new PluginBridge(this);
this.browser.OnFrameLoaded(OnFrameLoaded);
this.browser.RegisterBridge("$TDP", Bridge);
Config.Load(configPath); Config.Load(configPath);
Config.InternalPluginChangedState += Config_InternalPluginChangedState; Config.PluginChangedState += Config_PluginChangedState;
} }
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){ private void Config_PluginChangedState(object sender, PluginChangedStateEventArgs e){
PluginChangedState?.Invoke(this, e); browser.ExecuteFunction("TDPF_setPluginState", e.Plugin, e.IsEnabled);
Config.Save(configPath); Config.Save(configPath);
} }
private void OnFrameLoaded(IFrame frame){
ExecutePlugins(frame, PluginEnvironment.Browser);
}
public bool IsPluginInstalled(string identifier){ public bool IsPluginInstalled(string identifier){
return plugins.Any(plugin => plugin.Identifier.Equals(identifier)); return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
} }
@@ -57,6 +67,24 @@ namespace TweetDuck.Plugins{
return plugins.Any(plugin => plugin.Environments.HasFlag(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)){
browser.ExecuteFunction("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){ public int GetTokenFromPlugin(Plugin plugin){
foreach(KeyValuePair<int, Plugin> kvp in tokens){ foreach(KeyValuePair<int, Plugin> kvp in tokens){
if (kvp.Value.Equals(plugin)){ if (kvp.Value.Equals(plugin)){

View File

@@ -10,7 +10,7 @@ using TweetDuck.Configuration;
using TweetDuck.Core; using TweetDuck.Core;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other; using TweetDuck.Core.Other;
using TweetDuck.Core.Other.Settings.Export; using TweetDuck.Core.Management;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Updates; using TweetDuck.Updates;
@@ -20,7 +20,7 @@ namespace TweetDuck{
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com"; public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.12.1.1"; public const string VersionTag = "1.13";
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");
@@ -32,6 +32,7 @@ namespace TweetDuck{
public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins"); public static readonly string PluginDataPath = Path.Combine(StoragePath, "TD_Plugins");
private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates"); private static readonly string InstallerPath = Path.Combine(StoragePath, "TD_Updates");
private static readonly string CefDataPath = Path.Combine(StoragePath, "TD_Chromium");
public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg"); public static string UserConfigFilePath => Path.Combine(StoragePath, "TD_UserConfig.cfg");
public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg"); public static string SystemConfigFilePath => Path.Combine(StoragePath, "TD_SystemConfig.cfg");
@@ -117,10 +118,10 @@ namespace TweetDuck{
SystemConfig = SystemConfig.Load(SystemConfigFilePath); SystemConfig = SystemConfig.Load(SystemConfigFilePath);
if (Arguments.HasFlag(Arguments.ArgImportCookies)){ if (Arguments.HasFlag(Arguments.ArgImportCookies)){
ExportManager.ImportCookies(); ProfileManager.ImportCookies();
} }
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){ else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)){
ExportManager.DeleteCookies(); ProfileManager.DeleteCookies();
} }
if (Arguments.HasFlag(Arguments.ArgUpdated)){ if (Arguments.HasFlag(Arguments.ArgUpdated)){
@@ -128,13 +129,15 @@ namespace TweetDuck{
} }
BrowserCache.RefreshTimer(); BrowserCache.RefreshTimer();
CefSharpSettings.WcfEnabled = false; CefSharpSettings.WcfEnabled = false;
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
CefSettings settings = new CefSettings{ CefSettings settings = new CefSettings{
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
UserAgent = BrowserUtils.HeaderUserAgent, UserAgent = BrowserUtils.HeaderUserAgent,
BrowserSubprocessPath = BrandName+".Browser.exe", BrowserSubprocessPath = BrandName+".Browser.exe",
CachePath = StoragePath, CachePath = StoragePath,
UserDataPath = CefDataPath,
LogFile = ConsoleLogFilePath, LogFile = ConsoleLogFilePath,
#if !DEBUG #if !DEBUG
LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable
@@ -191,18 +194,6 @@ namespace TweetDuck{
} }
} }
public static void ResetConfig(){
try{
File.Delete(UserConfigFilePath);
File.Delete(UserConfig.GetBackupFile(UserConfigFilePath));
}catch(Exception e){
Reporter.HandleException("Configuration Reset Error", "Could not delete configuration files to reset the options.", true, e);
return;
}
UserConfig.Reload();
}
public static void Restart(params string[] extraArgs){ public static void Restart(params string[] extraArgs){
CommandLineArgs args = Arguments.GetCurrentClean(); CommandLineArgs args = Arguments.GetCurrentClean();
CommandLineArgs.ReadStringArray('-', extraArgs, args); CommandLineArgs.ReadStringArray('-', extraArgs, args);
@@ -210,14 +201,15 @@ namespace TweetDuck{
} }
public static void RestartWithArgs(CommandLineArgs args){ public static void RestartWithArgs(CommandLineArgs args){
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault(); FormBrowser browserForm = FormManager.TryFind<FormBrowser>();
if (browserForm == null)return;
if (browserForm != null){
browserForm.ForceClose(); browserForm.ForceClose();
ExitCleanup(); ExitCleanup();
RestartWithArgsInternal(args); RestartWithArgsInternal(args);
} }
}
private static void RestartWithArgsInternal(CommandLineArgs args){ private static void RestartWithArgsInternal(CommandLineArgs args){
args.AddFlag(Arguments.ArgRestart); args.AddFlag(Arguments.ArgRestart);

View File

@@ -19,7 +19,7 @@ namespace TweetDuck.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources { internal class Resources {
@@ -60,6 +60,16 @@ namespace TweetDuck.Properties {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] avatar {
get {
object obj = ResourceManager.GetObject("avatar", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary> /// </summary>
@@ -89,5 +99,15 @@ namespace TweetDuck.Properties {
return ((System.Drawing.Icon)(obj)); return ((System.Drawing.Icon)(obj));
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] spinner {
get {
object obj = ResourceManager.GetObject("spinner", resourceCulture);
return ((byte[])(obj));
}
}
} }
} }

View File

@@ -118,6 +118,9 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="avatar" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\avatar.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
@@ -127,4 +130,7 @@
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>..\Resources\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>
<data name="spinner" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\spinner.apng;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root> </root>

View File

@@ -1,6 +1,6 @@
# Support # Support
[Follow TweetDuck on Twitter](https://twitter.com/TryTweetDuck) &nbsp;|&nbsp; [Support via PayPal](https://paypal.me/chylex) &nbsp;|&nbsp; [Support via Patreon](https://www.patreon.com/chylex) [Follow TweetDuck on Twitter](https://twitter.com/TryMyAwesomeApp) &nbsp;|&nbsp; [Support via PayPal](https://paypal.me/chylex) &nbsp;|&nbsp; [Support via Patreon](https://www.patreon.com/chylex)
# Build Instructions # Build Instructions

View File

@@ -23,6 +23,10 @@ namespace TweetDuck{
} }
public bool Log(string data){ public bool Log(string data){
#if DEBUG
Debug.WriteLine(data);
#endif
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
if (!File.Exists(logFile)){ if (!File.Exists(logFile)){

View File

@@ -42,6 +42,14 @@ enabled(){
col.model.setHasSound(prevSound); col.model.setHasSound(prevSound);
}, 1); }, 1);
} }
// ========================
// D key - trigger debugger
// ========================
else if (e.keyCode === 68){
debugger;
}
} }
}; };
} }

View File

@@ -8,7 +8,7 @@ Edit layout & design
chylex chylex
[version] [version]
1.1.8 1.2.3
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -7,6 +7,7 @@ enabled(){
this.defaultConfig = { this.defaultConfig = {
_theme: "light", _theme: "light",
themeOverride: false,
columnWidth: "310px", columnWidth: "310px",
fontSize: "12px", fontSize: "12px",
hideTweetActions: true, hideTweetActions: true,
@@ -125,7 +126,7 @@ enabled(){
let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>'); let itemEditDesign = $('<li class="is-selectable"><a href="#" data-action>Edit layout &amp; design</a></li>');
itemEditDesign.insertAfter(itemTD); itemEditDesign.insertAfter(itemTD);
itemEditDesign.on("click", "a", this.openEditDesignDialog); itemEditDesign.on("click", "a", this.configure.bind(this));
itemEditDesign.hover(function(){ itemEditDesign.hover(function(){
$(this).addClass("is-selected"); $(this).addClass("is-selected");
@@ -147,7 +148,7 @@ enabled(){
}, 1); // delays the slight lag caused by saving and reinjection }, 1); // delays the slight lag caused by saving and reinjection
}; };
var customDesignModal = TD.components.BaseModal.extend(function(){ this.customDesignModal = TD.components.BaseModal.extend(function(){
let modal = $("#td-design-plugin-modal"); let modal = $("#td-design-plugin-modal");
this.setAndShowContainer(modal, false); this.setAndShowContainer(modal, false);
@@ -237,13 +238,26 @@ enabled(){
}); });
// THEMES // THEMES
modal.find("[data-td-theme='"+TD.settings.getTheme()+"']").prop("checked", true); let selectedTheme = me.config.themeOverride || TD.settings.getTheme();
modal.find("[data-td-theme='"+selectedTheme+"']").prop("checked", true);
modal.find("[data-td-theme]").change(function(){ modal.find("[data-td-theme]").change(function(){
me.config._theme = $(this).attr("data-td-theme"); let theme = $(this).attr("data-td-theme");
me.config._theme = theme;
if (theme === "black"){
me.config.themeOverride = theme;
theme = "dark";
}
else{
me.config.themeOverride = false;
}
setTimeout(function(){ setTimeout(function(){
if (theme != TD.settings.getTheme()){
$(document).trigger("uiToggleTheme"); $(document).trigger("uiToggleTheme");
}
me.saveConfig(); me.saveConfig();
me.reinjectAll(); me.reinjectAll();
}, 1); }, 1);
@@ -261,8 +275,6 @@ enabled(){
} }
}); });
this.openEditDesignDialog = () => new customDesignModal();
// animation optimization // animation optimization
this.optimizations = null; this.optimizations = null;
this.optimizationTimer = null; this.optimizationTimer = null;
@@ -335,6 +347,14 @@ enabled(){
this.css = window.TDPF_createCustomStyle(this); this.css = window.TDPF_createCustomStyle(this);
if (this.theme){
this.theme.remove();
}
if (this.config.themeOverride){
this.theme = window.TDPF_createCustomStyle(this);
}
if (this.icons){ if (this.icons){
document.head.removeChild(this.icons); document.head.removeChild(this.icons);
this.icons = null; this.icons = null;
@@ -364,15 +384,21 @@ enabled(){
if (this.config.themeColorTweaks){ if (this.config.themeColorTweaks){
switch(TD.settings.getTheme()){ switch(TD.settings.getTheme()){
case "dark": case "dark":
if (this.config.themeOverride === "black"){
this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }"); this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
this.css.insert(".column-drag-handle { opacity: 0.5 !important }"); this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
this.css.insert(".column-drag-handle:hover { opacity: 1 !important }"); this.css.insert(".column-drag-handle:hover { opacity: 1 !important }");
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #666 !important }"); this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #666 !important }");
notificationScrollbarColor = "666"; notificationScrollbarColor = "666";
}
else{
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-track, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-track { border-left-color: #14171A !important }");
}
break; break;
case "light": case "light":
this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb, .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb { background-color: #d2d6da !important }"); this.css.insert(".scroll-styled-v:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h:not(.scroll-alt)::-webkit-scrollbar-thumb:not(:hover) { background-color: #d2d6da !important }");
this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }"); this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }");
notificationScrollbarColor = "a5aeb5"; notificationScrollbarColor = "a5aeb5";
break; break;
@@ -405,87 +431,87 @@ enabled(){
if (this.config.revertIcons){ if (this.config.revertIcons){
let iconData = [ let iconData = [
[ ".icon-twitter-bird", "00" ], [ "twitter-bird", "00" ],
[ ".icon-mention", "01" ], [ "mention", "01" ],
[ ".icon-following", "02" ], [ "following", "02" ],
[ ".icon-message", "03" ], [ "message", "03" ],
[ ".icon-home", "04" ], [ "home", "04" ],
[ ".icon-hashtag", "05" ], [ "hashtag", "05" ],
[ ".icon-reply", "06" ], [ "reply", "06" ],
[ ".icon-favorite", "55" ], [ "favorite", "55" ],
[ ".icon-retweet", "08" ], [ "retweet", "08" ],
[ ".icon-drafts", "09" ], [ "drafts", "09" ],
[ ".icon-search", "0a" ], [ "search", "0a" ],
[ ".icon-trash", "0c" ], [ "trash", "0c" ],
[ ".icon-close", "0d" ], [ "close", "0d" ],
[ ".icon-arrow-r:before,.Icon--caretRight", "0e" ], [ "arrow-r:before,.Icon--caretRight", "0e" ],
[ ".icon-arrow-l:before,.Icon--caretLeft", "0f" ], [ "arrow-l:before,.Icon--caretLeft", "0f" ],
[ ".icon-protected", "13" ], [ "protected", "13" ],
[ ".icon-list", "14" ], [ "list", "14" ],
[ ".icon-camera", "15" ], [ "camera", "15" ],
[ ".icon-more", "16" ], [ "more", "16" ],
[ ".icon-settings", "18" ], [ "settings", "18" ],
[ ".icon-notifications", "19" ], [ "notifications", "19" ],
[ ".icon-user-dd", "1a" ], [ "user-dd", "1a" ],
[ ".icon-activity", "1c" ], [ "activity", "1c" ],
[ ".icon-trending", "1d" ], [ "trending", "1d" ],
[ ".icon-minus", "1e" ], [ "minus", "1e" ],
[ ".icon-plus", "1f" ], [ "plus", "1f" ],
[ ".icon-geo", "20" ], [ "geo", "20" ],
[ ".icon-check", "21" ], [ "check", "21" ],
[ ".icon-schedule", "22" ], [ "schedule", "22" ],
[ ".icon-dot", "23" ], [ "dot", "23" ],
[ ".icon-user", "24" ], [ "user", "24" ],
[ ".icon-content", "25" ], [ "content", "25" ],
[ ".icon-arrow-d:before,.Icon--caretDown", "26" ], [ "arrow-d:before,.Icon--caretDown", "26" ],
[ ".icon-arrow-u", "27" ], [ "arrow-u", "27" ],
[ ".icon-share", "28" ], [ "share", "28" ],
[ ".icon-info", "29" ], [ "info", "29" ],
[ ".icon-verified", "2a" ], [ "verified", "2a" ],
[ ".icon-translator", "2b" ], [ "translator", "2b" ],
[ ".icon-blocked", "2c" ], [ "blocked", "2c" ],
[ ".icon-constrain", "2d" ], [ "constrain", "2d" ],
[ ".icon-play-video", "2e" ], [ "play-video", "2e" ],
[ ".icon-empty", "2f" ], [ "empty", "2f" ],
[ ".icon-clear-input", "30" ], [ "clear-input", "30" ],
[ ".icon-compose", "31" ], [ "compose", "31" ],
[ ".icon-mark-read", "32" ], [ "mark-read", "32" ],
[ ".icon-arrow-r-double", "33" ], [ "arrow-r-double", "33" ],
[ ".icon-arrow-l-double", "34" ], [ "arrow-l-double", "34" ],
[ ".icon-follow", "35" ], [ "follow", "35" ],
[ ".icon-image", "36" ], [ "image", "36" ],
[ ".icon-popout", "37" ], [ "popout", "37" ],
[ ".icon-move", "39" ], [ "move", "39" ],
[ ".icon-compose-grid", "3a" ], [ "compose-grid", "3a" ],
[ ".icon-compose-minigrid", "3b" ], [ "compose-minigrid", "3b" ],
[ ".icon-compose-list", "3c" ], [ "compose-list", "3c" ],
[ ".icon-edit", "40" ], [ "edit", "40" ],
[ ".icon-clear-timeline", "41" ], [ "clear-timeline", "41" ],
[ ".icon-sliders", "42" ], [ "sliders", "42" ],
[ ".icon-custom-timeline", "43" ], [ "custom-timeline", "43" ],
[ ".icon-compose-dm", "44" ], [ "compose-dm", "44" ],
[ ".icon-bg-dot", "45" ], [ "bg-dot", "45" ],
[ ".icon-user-team-mgr", "46" ], [ "user-team-mgr", "46" ],
[ ".icon-user-switch", "47" ], [ "user-switch", "47" ],
[ ".icon-conversation", "48" ], [ "conversation", "48" ],
[ ".icon-dataminr", "49" ], [ "dataminr", "49" ],
[ ".icon-link", "4a", ], [ "link", "4a", ],
[ ".icon-flash", "50" ], [ "flash", "50" ],
[ ".icon-pointer-u", "51" ], [ "pointer-u", "51" ],
[ ".icon-analytics", "54" ], [ "analytics", "54" ],
[ ".icon-heart", "55" ], [ "heart", "55" ],
[ ".icon-calendar", "56" ], [ "calendar", "56" ],
[ ".icon-attachment", "57" ], [ "attachment", "57" ],
[ ".icon-play", "58" ], [ "play", "58" ],
[ ".icon-bookmark", "59" ], [ "bookmark", "59" ],
[ ".icon-play-badge", "60" ], [ "play-badge", "60" ],
[ ".icon-gif-badge", "61" ], [ "gif-badge", "61" ],
[ ".icon-poll", "62" ], [ "poll", "62" ],
[ ".icon-heart-filled", "55" ], [ "heart-filled", "55" ],
[ ".icon-retweet-filled", "08" ], [ "retweet-filled", "08" ],
[ ".icon-list-filled", "14" ], [ "list-filled", "14" ],
[ ".icon-user-filled", "35" ], [ "user-filled", "35" ],
]; ];
this.icons = document.createElement("style"); this.icons = document.createElement("style");
@@ -497,9 +523,12 @@ enabled(){
font-style: normal; font-style: normal;
} }
${iconData.map(entry => `#tduck ${entry[0]}:before{content:\"\\f0${entry[1]}\";font-family:_of!important}`).join("")} ${iconData.map(entry => `#tduck .icon-${entry[0]}:before{content:\"\\f0${entry[1]}\";font-family:_of!important}`).join("")}
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important } .drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
.app-search-fake .icon { margin-top: -3px !important }
#tduck .search-input-control .icon { font-size: 20px !important; top: -4px !important }
.column-header .column-type-icon { bottom: 26px !important } .column-header .column-type-icon { bottom: 26px !important }
.is-options-open .column-type-icon { bottom: 25px !important } .is-options-open .column-type-icon { bottom: 25px !important }
@@ -510,6 +539,14 @@ ${iconData.map(entry => `#tduck ${entry[0]}:before{content:\"\\f0${entry[1]}\";f
document.head.appendChild(this.icons); document.head.appendChild(this.icons);
} }
if (this.config.themeOverride === "black"){
$TDP.readFileRoot(this.$token, "theme.black.css").then(contents => {
if (this.theme){
this.theme.element.innerHTML = contents;
}
});
}
if (this.config.columnWidth[0] === '/'){ if (this.config.columnWidth[0] === '/'){
let cols = this.config.columnWidth.slice(1); let cols = this.config.columnWidth.slice(1);
@@ -561,8 +598,14 @@ ${this.config.revertIcons ? `
#tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important} #tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
` : ``} ` : ``}
${this.config.themeOverride === "black" ? `
html.dark a, html.dark a:hover, html.dark a:focus, html.dark a:active { color: #8bd }
.btn-neutral-positive { color: #8bd !important }
.quoted-tweet { border-color: #292f33 !important }
` : ``}
${notificationScrollbarColor ? ` ${notificationScrollbarColor ? `
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} !important } .scroll-styled-v::-webkit-scrollbar-thumb:not(:hover), .scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #${notificationScrollbarColor} !important }
` : ``} ` : ``}
</style>`); </style>`);
}; };
@@ -605,7 +648,7 @@ ready(){
TD.components.GlobalSettings.prototype.switchTab = function(tab){ TD.components.GlobalSettings.prototype.switchTab = function(tab){
if (tab === "tdp-edit-design"){ if (tab === "tdp-edit-design"){
me.openEditDesignDialog(); me.configure();
} }
else{ else{
return me.prevFuncSettingsSwitchTab.apply(this, arguments); return me.prevFuncSettingsSwitchTab.apply(this, arguments);
@@ -613,11 +656,19 @@ ready(){
}; };
} }
configure(){
new this.customDesignModal();
}
disabled(){ disabled(){
if (this.css){ if (this.css){
this.css.remove(); this.css.remove();
} }
if (this.theme){
this.theme.remove();
}
if (this.icons){ if (this.icons){
document.head.removeChild(this.icons); document.head.removeChild(this.icons);
} }

View File

@@ -15,6 +15,10 @@
<label class="txt-uppercase touch-larger-label"> <label class="txt-uppercase touch-larger-label">
<b>Theme</b> <b>Theme</b>
</label> </label>
<label class="radio">
<input data-td-theme="black" class="js-theme-radio touch-larger-label" name="theme" type="radio">
Black
</label>
<label class="radio"> <label class="radio">
<input data-td-theme="dark" class="js-theme-radio touch-larger-label" name="theme" type="radio"> <input data-td-theme="dark" class="js-theme-radio touch-larger-label" name="theme" type="radio">
Dark Dark
@@ -164,6 +168,10 @@
height: 380px; height: 380px;
} }
#edit-design-panel .mdl-inner {
padding-top: 0;
}
#edit-design-panel-inner-cols { #edit-design-panel-inner-cols {
padding: 0 6px; padding: 0 6px;
} }
@@ -233,7 +241,7 @@
} }
.td-avatar-shape-item-outer label { .td-avatar-shape-item-outer label {
margin: 10px 0 0; margin: 10px 0 0 !important;
} }
.td-avatar-shape { .td-avatar-shape {

View File

@@ -0,0 +1,851 @@
html.dark{color:#e1e8ed}
html.dark .is-inverted-dark .stream-item{background-color:#fff}
html.dark::selection{background:#e1e8ed;color:#111}
html.dark a{color:#8bd}
html.dark a:hover,html.dark a:focus,html.dark a:active{color:#8bd}
html.dark .txt-mute{color:#8899a6}
html.dark .txt-mute a:not(:hover):not(:focus){color:#8899a6}
html.dark .txt-mute-text-only{color:#8899a6}
html.dark .color-twitter-darker-gray{color:#657786}
html.dark .color-twitter-white{color:#fff}
html.dark .color-twitter-gray{color:#AAB8C2}
html.dark .color-twitter-blue{color:#1DA1F2}
html.dark .color-twitter-red{color:#E0245E}
html.dark .color-twitter-deep-red{color:#A01744}
html.dark .color-twitter-green{color:#17BF63}
html.dark .color-twitter-deep-green{color:#008951}
html.dark .color-twitter-deep-black{color:#14171A}
html.dark .color-twitter-dark-black{color:#292F33}
html.dark .color-twitter-dark-gray{color:#8899A6}
html.dark .color-twitter-black{color:#000}
html.dark .color-twitter-yellow{color:#FFAD1F}
html.dark .bg-color-twitter-white{background-color:#fff}
html.dark .bg-color-twitter-blue{background-color:#1DA1F2}
html.dark .bg-color-twitter-medium-blue{background-color:#1DA1F2}
html.dark .bg-color-twitter-faded-yellow{background-color:#FFE8B6}
html.dark .bg-color-twitter-deep-black{background-color:#292F33}
html.dark .bg-color-twitter-lightest-gray{background-color:#F5F8FA}
html.dark .link-hover-override:hover .link-hover-target{color:#8bd}
html.dark .scroll-styled-v::-webkit-scrollbar-track,html.dark .scroll-styled-h::-webkit-scrollbar-track{border-left:1px solid #292F33}
html.dark .scroll-styled-v::-webkit-scrollbar-thumb,html.dark .scroll-styled-h::-webkit-scrollbar-thumb{background-color:#657786}
html.dark .scroll-styled-v::-webkit-scrollbar-thumb:hover,html.dark .scroll-styled-h::-webkit-scrollbar-thumb:hover{background-color:#8899a6}
html.dark .scroll-alt::-webkit-scrollbar-track{border-color:#eaeaea}
html.dark .scroll-alt::-webkit-scrollbar-thumb{background-color:#e1e8ed}
html.dark .scroll-alt::-webkit-scrollbar-thumb:hover{background-color:#657786}
html.dark .scroll-conversation{background:#292F33}
html.dark .is-inverted-dark .column-scroller::-webkit-scrollbar-track{border-left-color:#e1e8ed}
html.dark .is-inverted-dark .column-scroller::-webkit-scrollbar-thumb{background-color:#ddd}
html.dark .is-inverted-dark .column-scroller::-webkit-scrollbar-thumb:hover{background-color:#8899a6}
html.dark .antiscroll-scrollbar{background:rgba(255,255,255,0.2)}
html.dark .is-loading{background-color:#fff}
html.dark .with-drop-shadow:after{box-shadow:inset 0 2px 1px rgba(17,17,17,0.25);border-top:1px solid rgba(17,17,17,0.25)}
html.dark .border-separated li{border-bottom:1px solid #CCD6DD}
html.dark .dark-border{border:1px solid #292f33}
html.dark .dark-border-top{border-top:1px solid #292f33}
html.dark .bs-1{box-shadow:0 1px 4px rgba(0,0,0,0.25)}
html.dark .is-inverted-dark{color:#292F33}
html.dark .is-inverted-dark a,html.dark .is-inverted-dark a:hover,html.dark .is-inverted-dark a:focus,html.dark .is-inverted-dark a:active{color:#3b94d9}
html.dark .is-inverted-dark .link-normal-dark,html.dark .is-inverted-dark .link-normal-dark:hover,html.dark .is-inverted-dark .link-normal-dark:focus,html.dark .is-inverted-dark .link-normal-dark:active{color:#111}
html.dark .is-inverted-dark .list-link,html.dark .is-inverted-dark .list-twitter-list,html.dark .is-inverted-dark .list-subtitle,html.dark .is-inverted-dark .list-account,html.dark .is-inverted-dark .list-listmember{color:#292F33}
html.dark .is-inverted-dark .txt-mute{color:#8899a6}
html.dark .is-inverted-dark .txt-mute a:not(:hover):not(:focus){color:#8899a6}
html.dark .is-inverted-dark .stream-item:not(.conversation-event),html.dark .is-inverted-dark .conversation-event+.stream-item:not(.conversation-event){border-color:#eaeaea}
html.dark .is-inverted-dark .account-link{color:#292F33}
html.dark .is-inverted-dark .account-bio{color:#8899a6}
html.dark .is-inverted-dark .with-drop-shadow:after{box-shadow:inset 0 2px 4px #ccd6dd;border-top:1px solid rgba(17,17,17,0.25)}
html.dark .is-inverted-dark .column-close-link{color:#66757f}
html.dark .is-inverted-dark .accordion{color:#111}
html.dark .is-inverted-dark .accordion-header{color:#111}
html.dark .is-inverted-dark .accordion-divider-t{border-top:1px solid #eaeaea}
html.dark .is-inverted-dark .accordion-header:hover{color:#111}
html.dark .is-inverted-dark .facet-type-content.is-active{background-color:rgba(210,155,154,0.2)}
html.dark .is-inverted-dark .facet-type-user.is-active{background-color:rgba(255,217,131,0.2)}
html.dark .is-inverted-dark .facet-type-location.is-active{background-color:rgba(118,194,158,0.2)}
html.dark .is-inverted-dark .facet-type-preferences.is-active{background-color:rgba(136,153,166,0.2)}
html.dark .is-inverted-dark .facet-type-engagement.is-active{background-color:#e1e8ed}
html.dark .is-inverted-dark .facet-type{border-bottom:1px solid #eaeaea}
html.dark .is-inverted-dark .accordion .is-active{color:#111}
html.dark .is-inverted-dark .accordion .is-active .accordion-header,html.dark .is-inverted-dark .accordion .is-active .accordion-header:hover{color:#111}
html.dark .is-inverted-dark .tweet-detail-wrapper{background:#F5F8FA}
html.dark .is-inverted-dark .scroll-conversation{background:#eaeaea}
html.dark .is-inverted-dark .tweet-detail-actions,html.dark .is-inverted-dark .tweet-stats,html.dark .is-inverted-dark .card-holder{border-top-color:#eaeaea}
html.dark .is-inverted-dark .tweet-detail-action{color:#8899a6}
html.dark .is-inverted-dark .tweet-detail-action:hover,html.dark .is-inverted-dark .tweet-detail-action:focus,html.dark .is-inverted-dark .tweet-detail-action:active,html.dark .is-inverted-dark .tweet-detail-action.is-selected{color:#292F33}
html.dark .is-inverted-dark .social-proof-for-tweet-title{background-color:#eaeaea;color:#777;border-bottom:#ddd}
html.dark .is-inverted-dark .rpl textarea{border:1px solid #e1e8ed;background:#fff;box-shadow:#8899a6 0 1px 0 inset}
html.dark .is-inverted-dark .media-badge{border-color:#e1e8ed;color:#8899a6}
html.dark .is-inverted-dark .media-badge:hover{background-color:#f5f8fa}
html.dark .icon-favorite-color{color:#E0245E}
html.dark .icon-follow-color{color:#50a5e6}
html.dark .icon-list-color{color:#66757f}
html.dark .icon-image-color{color:#66757f}
html.dark .icon-mention-color{color:#66757f}
html.dark .icon-unread-color{color:#50a5e6}
html.dark .icon-remove-color{color:#dd2e44}
html.dark .icon-submit-color{color:#77b255}
html.dark .icon-retweet-color{color:#17BF63}
html.dark .icon-twitter-blue-color{color:#2b7bb9}
html.dark .btn:hover{color:#1DA1F2;background-color:#292F33}
html.dark .btn:focus{color:#1DA1F2;background-color:#292F33;box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
html.dark .btn:active,html.dark .btn.is-selected{color:#1DA1F2;background-color:#292F33}
html.dark .btn[disabled],html.dark .btn[disabled]:hover,html.dark .btn[disabled]:active,html.dark .btn.is-disabled,html.dark .btn.is-disabled:hover,html.dark .btn.is-disabled:focus,html.dark .btn.is-disabled:active{color:#1DA1F2}
html.dark .btn-on-dark:focus{box-shadow:0 0 0 1px #292F33,0 0 0 3px #71C9F8}
html.dark .mdl-content .btn-on-dark:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
html.dark .is-inverted-dark .btn:hover,html.dark .is-inverted-dark .btn:focus,html.dark .is-inverted-dark .btn:active,html.dark .is-inverted-dark .btn.is-selected{background-color:#F2F9FF}
html.dark .is-inverted-dark .btn-positive:hover,html.dark .is-inverted-dark .btn-positive-alt:hover,html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#005FD1}
html.dark .is-inverted-dark .btn-positive:focus,html.dark .is-inverted-dark .btn-positive-alt:focus,html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#005FD1}
html.dark .is-inverted-dark .btn-positive:active,html.dark .is-inverted-dark .btn-positive-alt:active,html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .btn-positive.is-selected,html.dark .is-inverted-dark .is-selected.btn-positive-alt,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#005FD1}
html.dark .is-inverted-dark .btn-positive-alt:hover,html.dark .is-inverted-dark .btn-fav.s-favorited:hover,html.dark .is-inverted-dark .s-following .follow-btn:hover,html.dark .s-following .is-inverted-dark .follow-btn:hover{background-color:#A01744}
html.dark .is-inverted-dark .btn-positive-alt:focus,html.dark .is-inverted-dark .btn-fav.s-favorited:focus,html.dark .is-inverted-dark .s-following .follow-btn:focus,html.dark .s-following .is-inverted-dark .follow-btn:focus{background-color:#A01744}
html.dark .is-inverted-dark .btn-positive-alt:active,html.dark .is-inverted-dark .btn-fav.s-favorited:active,html.dark .is-inverted-dark .s-following .follow-btn:active,html.dark .s-following .is-inverted-dark .follow-btn:active,html.dark .is-inverted-dark .btn-positive-alt.is-selected,html.dark .is-inverted-dark .is-selected.btn-fav.s-favorited,html.dark .is-inverted-dark .s-following .is-selected.follow-btn,html.dark .s-following .is-inverted-dark .is-selected.follow-btn{background-color:#A01744}
html.dark .is-inverted-dark .btn-negative:hover{background-color:#A01744}
html.dark .is-inverted-dark .btn-negative:focus{background-color:#A01744}
html.dark .is-inverted-dark .btn-negative:active,html.dark .is-inverted-dark .btn-negative.is-selected{background-color:#A01744}
html.dark .is-inverted-dark .btn-tertiary:hover{background-color:#F5F8FA}
html.dark .is-inverted-dark .btn-tertiary:focus{background-color:#F5F8FA}
html.dark .is-inverted-dark .btn-tertiary:active,html.dark .is-inverted-dark .btn-tertiary.is-selected{background-color:#F5F8FA}
html.dark .btn-positive,html.dark .btn-positive-alt,html.dark .btn-fav.s-favorited,html.dark .s-following .follow-btn{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
html.dark .btn-positive:hover,html.dark .btn-positive-alt:hover,html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
html.dark .btn-positive:focus,html.dark .btn-positive-alt:focus,html.dark .btn-fav.s-favorited:focus,html.dark .s-following .follow-btn:focus{color:#fff;background-color:#005FD1;border:1px solid #005FD1;box-shadow:0 0 0 1px #fff,0 0 0 3px #71C9F8}
html.dark .btn-positive:active,html.dark .btn-positive-alt:active,html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .btn-positive.is-selected,html.dark .is-selected.btn-positive-alt,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#005FD1;border:1px solid #005FD1}
html.dark .btn-positive[disabled],html.dark [disabled].btn-positive-alt,html.dark [disabled].btn-fav.s-favorited,html.dark .s-following [disabled].follow-btn,html.dark .btn-positive[disabled]:hover,html.dark [disabled].btn-positive-alt:hover,html.dark [disabled].btn-fav.s-favorited:hover,html.dark .s-following [disabled].follow-btn:hover,html.dark .btn-positive[disabled]:active,html.dark [disabled].btn-positive-alt:active,html.dark [disabled].btn-fav.s-favorited:active,html.dark .s-following [disabled].follow-btn:active,html.dark .btn-positive.is-disabled,html.dark .is-disabled.btn-positive-alt,html.dark .is-disabled.btn-fav.s-favorited,html.dark .s-following .is-disabled.follow-btn,html.dark .btn-positive.is-disabled:hover,html.dark .is-disabled.btn-positive-alt:hover,html.dark .is-disabled.btn-fav.s-favorited:hover,html.dark .s-following .is-disabled.follow-btn:hover,html.dark .btn-positive.is-disabled:focus,html.dark .is-disabled.btn-positive-alt:focus,html.dark .is-disabled.btn-fav.s-favorited:focus,html.dark .s-following .is-disabled.follow-btn:focus,html.dark .btn-positive.is-disabled:active,html.dark .is-disabled.btn-positive-alt:active,html.dark .is-disabled.btn-fav.s-favorited:active,html.dark .s-following .is-disabled.follow-btn:active{color:#fff;background-color:#1DA1F2;border:1px solid #1DA1F2}
html.dark .btn-compose{color:#fff;background-color:#2b7bb9}
html.dark .btn-compose:hover{color:#fff;background-color:#2b7bb9}
html.dark .btn-compose:focus{color:#fff;background-color:#2b7bb9}
html.dark .btn-compose:active,html.dark .btn-compose.is-selected{color:#fff;background-color:#2b7bb9}
html.dark .btn-positive-alt:hover,html.dark .btn-fav.s-favorited:hover,html.dark .s-following .follow-btn:hover{color:#fff;background-color:#A01744;border:#A01744}
html.dark .btn-positive-alt:active,html.dark .btn-fav.s-favorited:active,html.dark .s-following .follow-btn:active,html.dark .btn-positive-alt.is-selected,html.dark .is-selected.btn-fav.s-favorited,html.dark .s-following .is-selected.follow-btn{color:#fff;background-color:#A01744;border:#A01744}
html.dark .btn-negative{border-color:#E0245E;color:#fff;background-color:#E0245E}
html.dark .btn-negative:hover{color:#fff;background-color:#A01744;border-color:#A01744}
html.dark .btn-negative:focus{color:#fff;background-color:#A01744;border-color:#A01744;box-shadow:0 0 0 1px #fff,0 0 0 3px #F6809A}
html.dark .btn-negative:active,html.dark .btn-negative.is-selected{color:#fff;background-color:#A01744;border-color:#A01744}
html.dark .btn-negative[disabled],html.dark .btn-negative[disabled]:hover,html.dark .btn-negative[disabled]:active,html.dark .btn-negative.is-disabled,html.dark .btn-negative.is-disabled:hover,html.dark .btn-negative.is-disabled:focus,html.dark .btn-negative.is-disabled:active{border-color:#E0245E;color:#fff;background-color:#E0245E}
html.dark .btn-tertiary{border-color:#657786;color:#657786}
html.dark .btn-tertiary:hover{color:#657786;background-color:#F5F8FA;border-color:#657786}
html.dark .btn-tertiary:focus{color:#657786;background-color:#F5F8FA;border-color:#657786;box-shadow:0 0 0 1px #fff,0 0 0 3px #CCD6DD}
html.dark .btn-tertiary:active,html.dark .btn-tertiary.is-selected{color:#657786;background-color:#F5F8FA;border-color:#657786}
html.dark .btn-tertiary[disabled],html.dark .btn-tertiary[disabled]:hover,html.dark .btn-tertiary[disabled]:active,html.dark .btn-tertiary.is-disabled,html.dark .btn-tertiary.is-disabled:hover,html.dark .btn-tertiary.is-disabled:focus,html.dark .btn-tertiary.is-disabled:active{color:#AAB8C2;border-color:#e1e8ed;background-color:#eaeaea}
html.dark .btn-on-blue{color:#fff;background-color:#66757f}
html.dark .btn-on-blue:hover{color:#fff;background-color:#66757f}
html.dark .btn-on-blue:focus{color:#fff;background-color:#66757f;box-shadow:0 0 2px 3px #50a5e6}
html.dark .btn-on-blue:active,html.dark .btn-on-blue.is-selected{color:#fff;background-color:#434c51}
html.dark .btn-on-blue[disabled],html.dark .btn-on-blue[disabled]:hover,html.dark .btn-on-blue[disabled]:active,html.dark .btn-on-blue.is-disabled,html.dark .btn-on-blue.is-disabled:hover,html.dark .btn-on-blue.is-disabled:focus,html.dark .btn-on-blue.is-disabled:active{color:#fff;background-color:#66757f}
html.dark .btn-neutral-negative{color:#d29b9a}
html.dark .btn-neutral-negative:hover,html.dark .btn-neutral-negative:focus{color:#d29b9a}
html.dark .btn-neutral-negative[disabled],html.dark .btn-neutral-negative[disabled]:hover,html.dark .btn-neutral-negative[disabled]:active,html.dark .btn-neutral-negative.is-disabled,html.dark .btn-neutral-negative.is-disabled:hover,html.dark .btn-neutral-negative.is-disabled:focus,html.dark .btn-neutral-negative.is-disabled:active{color:#d29b9a}
html.dark .btn-neutral-positive{color:#8bd}
html.dark .btn-neutral-positive:hover,html.dark .btn-neutral-positive:focus{color:#8bd}
html.dark .btn-neutral-positive[disabled],html.dark .btn-neutral-positive[disabled]:hover,html.dark .btn-neutral-positive[disabled]:active,html.dark .btn-neutral-positive.is-disabled,html.dark .btn-neutral-positive.is-disabled:hover,html.dark .btn-neutral-positive.is-disabled:focus,html.dark .btn-neutral-positive.is-disabled:active{color:#8bd}
html.dark .btn-options-tray{color:#e1e8ed}
html.dark .btn-options-tray:hover,html.dark .btn-options-tray:focus{color:#8bd}
html.dark .btn-options-tray[disabled],html.dark .btn-options-tray[disabled]:hover,html.dark .btn-options-tray[disabled]:active,html.dark .btn-options-tray.is-disabled,html.dark .btn-options-tray.is-disabled:hover,html.dark .btn-options-tray.is-disabled:focus,html.dark .btn-options-tray.is-disabled:active{color:#8bd}
html.dark .btn-bg-positive{background-color:rgba(102,117,127,0.5)}
html.dark .btn-bg-positive:hover,html.dark .btn-bg-positive:focus{background-color:rgba(102,117,127,0.5)}
html.dark .btn-bg-positive[disabled],html.dark .btn-bg-positive[disabled]:hover,html.dark .btn-bg-positive[disabled]:active,html.dark .btn-bg-positive.is-disabled,html.dark .btn-bg-positive.is-disabled:hover,html.dark .btn-bg-positive.is-disabled:focus,html.dark .btn-bg-positive.is-disabled:active{background-color:rgba(102,117,127,0.5)}
html.dark .btn-bg-negative{background-color:#5d5457}
html.dark .btn-bg-negative:hover,html.dark .btn-bg-negative:focus{background-color:#5d5457}
html.dark .btn-bg-negative[disabled],html.dark .btn-bg-negative[disabled]:hover,html.dark .btn-bg-negative[disabled]:active,html.dark .btn-bg-negative.is-disabled,html.dark .btn-bg-negative.is-disabled:hover,html.dark .btn-bg-negative.is-disabled:focus,html.dark .btn-bg-negative.is-disabled:active{background-color:#5d5457}
html.dark .btn-bg-white{background-color:#fff;color:#55acee}
html.dark .btn-bg-white:hover,html.dark .btn-bg-white:focus{background-color:#fff;color:#55acee}
html.dark .follow-btn .icon,html.dark .follow-btn .Icon{color:#1DA1F2}
html.dark .input-group-button{border:1px solid #e1e8ed}
html.dark .account-profile-header{background-color:#1DA1F2}
html.dark .account-settings-bt{border-top:1px solid #e1e8ed}
html.dark .account-settings-bb{border-bottom:1px solid #e1e8ed}
html.dark .account-stats a{color:#66757f}
html.dark .account-stats a:hover{color:#2b7bb9}
html.dark .column{background-color:#222426}
html.dark .column.is-focused{box-shadow:0 0 0 6px #7aa2c0}
html.dark .column-background-fill{background-color:#F5F8FA}
html.dark .more-tweets-glow{background-color:#55acee;background:radial-gradient(ellipse farthest-corner at 50% 100%,#55acee 0%,#55acee 25%,rgba(255,255,255,0) 75%)}
html.dark .more-tweets-btn{box-shadow:0 2px 0 rgba(0,0,0,0.2)}
html.dark .more-tweets-btn:active,html.dark .more-tweets-btn:focus{box-shadow:0 1px 0 rgba(0,0,0,0.3)}
html.dark .more-tweets-btn-container--mouse-release .more-tweets-btn,html.dark .more-tweets-btn-container--loading .more-tweets-btn{background-color:#66757f}
html.dark .drag-drop-indicator{background-color:#3b94d9;color:#fff}
html.dark .location-form .icon-close,html.dark .location-form .Icon--close{background-color:rgba(255,255,255,0.35)}
html.dark .search-filter-callout-triangle{border-color:transparent transparent #55acee}
html.dark .live-video-timelines{background-color:#292F33;border-bottom:1px solid #292F33}
html.dark .live-video-timelines button{color:#8bd;background-color:#292F33}
html.dark .live-video-timelines button:active,html.dark .live-video-timelines button:focus,html.dark .live-video-timelines button:hover{background-color:#292F33;color:#8bd}
html.dark .column-type-scheduled{background-color:#292F33}
html.dark .column-header{background-color:#292F33}
html.dark .is-inverted-dark .column-header{border-bottom:1px solid #ddd}
html.dark .is-inverted-dark .column-title-edit-box{color:#111;background-color:#fff;border-color:#e1e8ed}
html.dark .column-header{border-bottom:1px solid #222426}
html.dark .column-header-temp{border-bottom:1px solid #ddd}
html.dark .column-title-edit-box{color:#e1e8ed;background-color:#14171A;border-color:#111}
html.dark .column-number{color:#66757f}
html.dark .is-new .column-type-icon{color:#55acee}
html.dark .column-header-link{color:#66757f}
html.dark .column-header-link:hover,html.dark .column-header-link:focus,html.dark .column-header-link:active{color:#fff}
html.dark .is-options-open .column-settings-link{background-color:#292f33;color:#8bd;border-color:#222426}
html.dark .is-options-open .column-settings-link:hover{color:#8bd}
html.dark .column-message{background-color:#292f33}
html.dark .filter-error{color:#fff;background-color:#be1931}
html.dark .facet-content{color:#d29b9a}
html.dark .facet-user{color:#ffd983}
html.dark .facet-action{color:#9cd1d4}
html.dark .facet-engagement{color:#8899a6}
html.dark .edit-conversation-name{border-bottom:1px solid #222426}
html.dark .column-options{background-color:#292f33}
html.dark .with-column-divider-bottom{border-bottom:1px solid #292F33}
html.dark .column-options .button-tray{background-color:#292F33}
html.dark .btn-options-unique{color:#e1e8ed}
html.dark .is-inverted-dark .column-options{background-color:#fff}
html.dark .is-inverted-dark .with-column-divider-bottom{border-bottom:1px solid #e1e8ed}
html.dark .column-nav-updates{color:#55acee}
html.dark .contributor-manager .link-complex{border:1px solid #e1e8ed}
html.dark .contributor-settings-role{border-bottom:1px solid #e1e8ed}
html.dark .contributor-row[data-state='settings']{background-color:#fff}
html.dark .contributor-row[data-state='confirmAdd']{background-color:#fff}
html.dark .contributor-row[data-state='confirmAdd-added']{background-color:#fff}
html.dark .contributor-row[data-state='confirmDeadmin']{background-color:#fff}
html.dark .contributor-row[data-state='confirmRemove'],html.dark .contributor-row[data-state='confirmRemove-removing']{background-color:#fff}
html.dark .contributor-row[data-state='confirmRemove-removing']{background-color:#fff}
html.dark .stream-item{border-bottom:1px solid #292F33;background-color:#222426}
html.dark .is-streamed{background-color:#4F0299}
html.dark .gap-chirp{background-color:#14171A;color:#8899a6}
html.dark .gap-chirp:hover .gap-chirp-text--with-size,html.dark .gap-chirp:active .gap-chirp-text--with-size{background:#1f2428}
html.dark .gap-chirp-text--with-size{border-color:#4b5964}
html.dark .is-inverted-dark .thread{background-color:#E1E8ED}
html.dark .thread{background-color:#3a3d42}
html.dark .list-item{color:#66757f}
html.dark .list-item:hover,html.dark .list-item:active,html.dark .list-item.is-selected{background-color:#55acee;color:#fff}
html.dark .list-item:hover:not(.is-selected){color:#66757f}
html.dark .list-item:hover .txt-mute,html.dark .list-item:active .txt-mute,html.dark .list-item.is-selected .txt-mute{color:#e1e8ed}
html.dark .list-item:hover .list-icon,html.dark .list-item:active .list-icon,html.dark .list-item.is-selected .list-icon{color:#fff}
html.dark .list-item:hover:not(.is-selected) .list-icon{color:#8899a6}
html.dark .list-icon{color:#8899a6}
html.dark .list-divider{border-top:1px solid rgba(17,17,17,0.25)}
html.dark .list-item-button{color:#aab8c2;background-color:#F5F8FA}
html.dark .is-touch-search .list-item:hover,html.dark .is-touch-search .list-item:active,html.dark .is-touch-search .list-item.is-selected{background-color:#55acee;color:#fff}
html.dark .is-touch-search .list-item:hover .list-icon,html.dark .is-touch-search .list-item:active .list-icon,html.dark .is-touch-search .list-item.is-selected .list-icon{color:#fff}
html.dark .avatar-border--2{border:2px solid #fff;background-color:#fff}
html.dark .account-link{color:#e1e8ed}
.on-blue html.dark .account-link{color:#fff}
.compose .quoted-tweet html.dark .account-link{color:#66757f}
html.dark .media-badge{border:1px solid #292F33;color:#999}
html.dark .media-badge:hover{background-color:#14171A}
html.dark .media-size-large-height::after,html.dark .media-item.media-size-large::after{background-image:linear-gradient(rgba(17,17,17,0.25),rgba(17,17,17,0))}
html.dark .media-sensitive{background:#292f33;color:#8899a6}
html.dark .media-sensitive-title{color:#e1e8ed}
html.dark .media-caret{border-color:#292F33 transparent transparent}
html.dark .video-overlay{color:#fff}
html.dark .is-inverted-dark .media-sensitive{background:#e1e8ed;color:#8899a6}
html.dark .is-inverted-dark .media-sensitive-title{color:#292f33}
html.dark .is-inverted-dark .triangle{border-color:#fff transparent transparent}
html.dark .is-inverted-dark .media-badge{border:1px solid #e1e8ed;color:#8899a6}
html.dark .is-inverted-dark .media-badge:hover{background-color:#f5f8fa}
html.dark .tweet-action,html.dark .tweet-detail-action,html.dark .dm-action{color:#8899a6}
html.dark .tweet-action:hover .icon-reply,html.dark .tweet-detail-action:hover .icon-reply,html.dark .dm-action:hover .icon-reply,html.dark .tweet-action:focus .icon-reply,html.dark .tweet-detail-action:focus .icon-reply,html.dark .dm-action:focus .icon-reply,html.dark .tweet-action:active .icon-reply,html.dark .tweet-detail-action:active .icon-reply,html.dark .dm-action:active .icon-reply,html.dark .tweet-action.is-selected .icon-reply,html.dark .is-selected.tweet-detail-action .icon-reply,html.dark .is-selected.dm-action .icon-reply{color:#1DA1F2}
html.dark .tweet-action:hover .icon-retweet,html.dark .tweet-detail-action:hover .icon-retweet,html.dark .dm-action:hover .icon-retweet,html.dark .tweet-action:focus .icon-retweet,html.dark .tweet-detail-action:focus .icon-retweet,html.dark .dm-action:focus .icon-retweet,html.dark .tweet-action:active .icon-retweet,html.dark .tweet-detail-action:active .icon-retweet,html.dark .dm-action:active .icon-retweet,html.dark .tweet-action.is-selected .icon-retweet,html.dark .is-selected.tweet-detail-action .icon-retweet,html.dark .is-selected.dm-action .icon-retweet{color:#17BF63}
html.dark .tweet-action:hover .icon-favorite,html.dark .tweet-detail-action:hover .icon-favorite,html.dark .dm-action:hover .icon-favorite,html.dark .tweet-action:focus .icon-favorite,html.dark .tweet-detail-action:focus .icon-favorite,html.dark .dm-action:focus .icon-favorite,html.dark .tweet-action:active .icon-favorite,html.dark .tweet-detail-action:active .icon-favorite,html.dark .dm-action:active .icon-favorite,html.dark .tweet-action.is-selected .icon-favorite,html.dark .is-selected.tweet-detail-action .icon-favorite,html.dark .is-selected.dm-action .icon-favorite{color:#E0245E}
html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action{color:#657786}
html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:hover,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:hover,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:hover,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:focus,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:focus,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:focus,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:active,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:active,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:active,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action.is-selected,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .is-selected.tweet-detail-action,html.dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .is-selected.dm-action{color:#8899a6}
html.dark .is-inverted-dark .tweet-action,html.dark .is-inverted-dark .tweet-detail-action,html.dark .is-inverted-dark .dm-action,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action{color:#8899a6}
html.dark .is-inverted-dark .tweet-action:hover,html.dark .is-inverted-dark .tweet-detail-action:hover,html.dark .is-inverted-dark .dm-action:hover,html.dark .is-inverted-dark .tweet-action:focus,html.dark .is-inverted-dark .tweet-detail-action:focus,html.dark .is-inverted-dark .dm-action:focus,html.dark .is-inverted-dark .tweet-action:active,html.dark .is-inverted-dark .tweet-detail-action:active,html.dark .is-inverted-dark .dm-action:active,html.dark .is-inverted-dark .tweet-action.is-selected,html.dark .is-inverted-dark .is-selected.tweet-detail-action,html.dark .is-inverted-dark .is-selected.dm-action,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:hover,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:hover,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:hover,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:focus,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:focus,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:focus,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action:active,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-detail-action:active,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .dm-action:active,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .tweet-action.is-selected,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .is-selected.tweet-detail-action,html.dark .is-inverted-dark .chirp-container .stream-item:not(:hover):not(.is-selected-tweet) .is-selected.dm-action{color:#111}
html.dark .is-inverted-dark .quoted-tweet{border-color:#E1E8ED;color:#66757f}
html.dark .is-retweet .icon-retweet-toggle{color:#17BF63}
html.dark .is-favorite .icon-favorite-toggle{color:#E0245E}
html.dark .is-minimalist .tweet-img{background:#292F33}
html.dark .is-selected-tweet{background:#292F33}
html.dark .in-tweet-divider:before{background:#292F33}
html.dark .tweet-translation-original-text{color:#8899a6}
html.dark .quoted-tweet{border:1px solid #E1E8ED;color:#66757f;border-color:#292F33;color:#999}
html.dark .scheduled-tweet{border:1px solid #292F33;color:#8899A6}
html.dark .stream-item .icon-edit,html.dark .stream-item .icon-trash{color:#657786}
html.dark .stream-item .icon-edit:hover,html.dark .stream-item .icon-trash:hover{color:#8899A6}
html.dark .tweet-detail-wrapper{background:#222426}
html.dark .tweet-stats{border-top:1px solid #292F33}
html.dark .tweet-stat{color:#8899a6}
html.dark .stat-word{color:#66757f}
.is-actionable:hover html.dark .stat-word{color:#8bd}
html.dark .tweet-detail-actions{border-top:1px solid #292F33}
html.dark .conversation-more{color:#8bd}
html.dark .column-detail .is-selected-tweet{background:#292F33}
html.dark .social-proof-for-tweet-title{background-color:#292F33;color:#8899a6;border-bottom:1px solid #292F33}
html.dark .is-inverted-dark .tweet-detail-reply .is-selected{color:#111}
html.dark .is-unread{background:#485865}
html.dark .is-unread .txt-mute,html.dark .is-unread .conversation-indicator{color:#e1e8ed}
html.dark .is-unread.is-selected-tweet{background:#66757f}
html.dark .is-inverted-dark .is-unread{background:#bbddf5}
html.dark .is-inverted-dark .is-unread .txt-mute,html.dark .is-inverted-dark .is-unread .conversation-indicator{color:#292F33}
html.dark .conversation-indicator{color:#8899a6}
html.dark .conversation-event{background-color:#292F33}
html.dark .conversation-event+.stream-item:not(.conversation-event),html.dark .conversation-event:first-child{border-top:1px solid #292F33}
html.dark .add-participant{background-color:#292F33}
html.dark .rpl{border-bottom:#292F33}
html.dark .rpl textarea{border:1px solid #292F33;background:#fff;box-shadow:inset 0 1px 1px rgba(17,17,17,0.5)}
html.dark .rpl input.over-char-count{color:#ea596e}
html.dark .spinner-button-with-progress{color:#fff}
html.dark .app-header{background-color:#292f33}
html.dark .app-title{background-color:#292f33}
html.dark .app-content{background-color:#14171A}
html.dark .app-columns-container{background-color:#14171A}
html.dark .app-navigator{background-color:#292f33}
html.dark .app-nav-link{color:#8899A6}
html.dark .app-nav-link:focus,html.dark .app-nav-link:active{color:#8899A6}
html.dark .app-nav-link.is-selected,html.dark .app-nav-link:hover{color:#fff}
html.dark .app-nav-tab{color:#8899A6}
html.dark .app-nav-tab:hover{color:#fff}
html.dark .app-nav-tab.is-selected{color:#292f33}
html.dark .app-nav-tab.is-selected:hover{color:#292f33}
html.dark .attach-compose-buttons .tweet-button{background-color:#485865}
html.dark .with-nav-border-t:before{border-top:1px solid #777}
html.dark .app-search-input,html.dark .app-search-fake{background-color:#14171A;color:#aab8c2;box-shadow:inset 0 1px 1px rgba(17,17,17,0.5)}
html.dark .app-search-input::-webkit-input-placeholder{color:#aab8c2}
html.dark .app-search-input::placeholder{color:#aab8c2}
html.dark .app-search-input:focus,html.dark .app-search-input.is-focused{border:1px solid #292F33;color:#292F33;background-color:#fff}
html.dark .app-search-fake{color:#777}
html.dark .app-search-button{color:#aab8c2}
html.dark .app-search-button:hover{color:#aab8c2}
html.dark .message-banner .dismiss{color:#292F33}
html.dark .typeahead{background-color:#fff;color:#292f33}
html.dark .typeahead .fullname{color:#292f33}
html.dark .typeahead .username{color:#8899a6}
html.dark .accordion,html.dark .accordion-popover{color:#e1e8ed}
html.dark .accordion-divider-t{border-top:1px solid #292F33}
html.dark .accordion-header{color:#e1e8ed}
html.dark .accordion-header:hover{color:#e1e8ed}
html.dark .facet-type{border-bottom:1px solid #292F33}
html.dark .facet-type-thumb-size{border-top:1px solid #292F33}
html.dark .facet-type.is-active{background-color:rgba(136,153,166,0.2)}
html.dark .facet-subtitle{color:#8bd}
html.dark .accordion .is-active{color:#e1e8ed}
html.dark .accordion .is-active .accordion-header,html.dark .accordion .is-active .accordion-header:hover{color:#e1e8ed}
html.dark .account-settings-row.is-highlighted{background-color:#F5F8FA;border-top:1px solid #CCD6DD}
html.dark .account-settings-row.is-highlighted:last-child{border-bottom:1px solid #CCD6DD}
html.dark .join-team{border-top:1px solid #E1E8ED;border-bottom:1px solid #E1E8ED}
html.dark .account-row-separator-b:after{background-color:#e1e8ed}
html.dark .separator-a:before{background-color:#e1e8ed}
html.dark .tooltip-inner{background-color:#111;color:#ddd}
html.dark .tooltip-arrow{border:5px dashed #111}
html.dark .bottom{border-bottom-color:#111}
html.dark .top{border-top-color:#111}
html.dark .left{border-left-color:#111}
html.dark .right{border-right-color:#111}
html.dark .stroke-twitter-light-gray{stroke:#CCD6DD}
html.dark .stroke-twitter-blue{stroke:#1DA1F2}
html.dark .stroke-twitter-yellow{stroke:#FFAD1F}
html.dark .stroke-twitter-red{stroke:#E0245E}
html.dark .numbered-badge{background-color:#55acee;color:#fff}
html.dark .numbered-badge-onheader{border:2px solid #292F33}
html.dark .numbered-badge-onnav{border:2px solid #292f33}
html.dark .is-open .drawer:after{box-shadow:2px 0 1px rgba(0,0,0,0.2)}
html.dark .drawer-header{border-bottom:1px solid #e1e8ed}
html.dark .dataminr{background-color:#f5f8fa}
html.dark .txt-dataminr{color:#8899a6}
html.dark .dataminr-title{background-color:#ccd6dd;color:#66757f}
html.dark .dataminr-search-terms-detail{color:#66757f}
html.dark .dataminr-separator{border-bottom:4px solid #e1e8ed}
html.dark .is-dataminr-tweet{background-color:#fff}
html.dark .dataminr-header,html.dark .dataminr-meta-link{color:#8899a6}
html.dark .dataminr-category-pill{color:#fff;background-color:#5585ad}
html.dark .dataminr-category-mn{background-color:#1F90BF}
html.dark .dataminr-category-mbg{background-color:#1F90BF}
html.dark .dataminr-category-ln{background-color:#1F90BF}
html.dark .dataminr-category-bg{background-color:#1F90BF}
html.dark .dataminr-category-rpr{background-color:#1F90BF}
html.dark .dataminr-category-er{background-color:#CC412E}
html.dark .dataminr-category-gov{background-color:#CC412E}
html.dark .dataminr-category-ngo{background-color:#CC412E}
html.dark .dataminr-category-spo{background-color:#8A64AD}
html.dark .dataminr-category-ent{background-color:#8A64AD}
html.dark .dataminr-category-uni{background-color:#CC412E}
html.dark .dataminr-category-bsn{background-color:#CC412E}
html.dark .dataminr-category-alt{background-color:#b26333}
html.dark .dataminr-category-ctr{background-color:#CC7332}
html.dark .dataminr-label{color:#e28409}
html.dark .dataminr-label-momentum{color:#5caee1}
html.dark .dataminr-map-img{border:1px solid #ccd6dd}
html.dark .dataminr-bio-count{color:#66757f}
html.dark .dataminr-user-profile{background-color:#fff}
html.dark .dataminr{background-color:#292f33}
html.dark .txt-dataminr{color:#aab8c2}
html.dark .dataminr-title{background-color:#657786;color:#e1e8ed}
html.dark .dataminr-search-terms-detail{color:#e1e8ed}
html.dark .is-dataminr-tweet{background-color:#444448}
html.dark .dataminr-separator{border-bottom:2px solid #444448}
html.dark .dataminr-header,html.dark .dataminr-meta-link{color:#ccd6dd}
html.dark .dataminr-map-img{border:1px solid #292F33}
html.dark .dataminr-label{color:#FFAD1F}
html.dark .dataminr-label-momentum{color:#55acee}
html.dark .dataminr-bio-count{color:#aab8c2}
html.dark .dataminr-user-profile{background-color:#111}
html.dark .dataminr-external-link{background-color:#222426}
html.dark .is-inverted-dark .dataminr{background-color:#f5f8fa}
html.dark .is-inverted-dark .txt-dataminr{color:#8899a6}
html.dark .is-inverted-dark .dataminr-title{background-color:#ccd6dd;color:#66757f}
html.dark .is-inverted-dark .dataminr-search-terms-detail{color:#66757f}
html.dark .is-inverted-dark .dataminr-separator{border-bottom:4px solid #e1e8ed}
html.dark .is-inverted-dark .is-dataminr-tweet{background-color:#fff}
html.dark .is-inverted-dark .dataminr-header,html.dark .is-inverted-dark .dataminr-meta-link{color:#8899a6}
html.dark .is-inverted-dark .dataminr-category-pill{color:#fff;background-color:#5585ad}
html.dark .is-inverted-dark .dataminr-category-mn{background-color:#1F90BF}
html.dark .is-inverted-dark .dataminr-category-mbg{background-color:#1F90BF}
html.dark .is-inverted-dark .dataminr-category-ln{background-color:#1F90BF}
html.dark .is-inverted-dark .dataminr-category-bg{background-color:#1F90BF}
html.dark .is-inverted-dark .dataminr-category-rpr{background-color:#1F90BF}
html.dark .is-inverted-dark .dataminr-category-er{background-color:#CC412E}
html.dark .is-inverted-dark .dataminr-category-gov{background-color:#CC412E}
html.dark .is-inverted-dark .dataminr-category-ngo{background-color:#CC412E}
html.dark .is-inverted-dark .dataminr-category-spo{background-color:#8A64AD}
html.dark .is-inverted-dark .dataminr-category-ent{background-color:#8A64AD}
html.dark .is-inverted-dark .dataminr-category-uni{background-color:#CC412E}
html.dark .is-inverted-dark .dataminr-category-bsn{background-color:#CC412E}
html.dark .is-inverted-dark .dataminr-category-alt{background-color:#b26333}
html.dark .is-inverted-dark .dataminr-category-ctr{background-color:#CC7332}
html.dark .is-inverted-dark .dataminr-label{color:#e28409}
html.dark .is-inverted-dark .dataminr-label-momentum{color:#5caee1}
html.dark .is-inverted-dark .dataminr-map-img{border:1px solid #ccd6dd}
html.dark .is-inverted-dark .dataminr-bio-count{color:#66757f}
html.dark .is-inverted-dark .dataminr-user-profile{background-color:#fff}
html.dark .info-caret{border-color:transparent #55acee transparent transparent}
html.dark .info-popover-close{color:#fff}
html.dark .info-popover-close:hover,html.dark .info-popover-close:active{color:#fff}
html.dark .info-popover-list-item:before{color:#88c9f9}
html.dark .info-popover-content{border:1px solid #fff}
html.dark .poll-bar{background-color:#8899A6}
html.dark .poll-bar--winner{background-color:#3b94d9}
html.dark .other-replies{color:#8899a6}
html.dark .other-replies-link,html.dark .other-replies-link:hover{color:#8bd}
html.dark .compose .other-replies,html.dark .inline-reply .other-replies{color:#8899A6}
html.dark .compose .other-replies-link,html.dark .compose .other-replies-link:hover,html.dark .inline-reply .other-replies-link,html.dark .inline-reply .other-replies-link:hover{color:#2b7bb9}
html.dark .ovl,html.dark .overlay{background:rgba(41,47,51,0.9)}
html.dark .overlay-opaque{background-color:#1c6399;background-image:radial-gradient(center center,ellipse cover,#1c6399,#274256)}
html.dark .seamful .modal-content{background:#fff}
html.dark .modal-content-with-border{border:1px solid #ccd6dd}
html.dark .mdl{background-color:#fff;box-shadow:0 0 10px rgba(17,17,17,0.5)}
html.dark .mdl-header{color:#8899a6}
html.dark .mdl-header-divider{border-bottom:1px solid #e1e8ed}
html.dark .mdl-content{border:1px solid #ccd6dd;background:#eaeaea}
html.dark .mdl-placeholder{color:#aab8c2;text-shadow:0 1px 0 rgba(255,255,255,0.8)}
html.dark .mdl-dismiss{color:#292F33}
html.dark .mdl-dismiss:hover{color:#292F33}
html.dark .mdl-btn-media,html.dark .is-inverted-light .mdl-btn-media{color:#fff}
html.dark .mdl-btn-media:hover,html.dark .mdl-btn-media:active,html.dark .mdl-btn-media:focus,html.dark .is-inverted-light .mdl-btn-media:hover,html.dark .is-inverted-light .mdl-btn-media:active{color:#fff}
html.dark .mdl-media-prev,html.dark .mdl-media-next{background:rgba(17,17,17,0.3)}
html.dark .mdl-column-med{background:#F5F8FA}
html.dark .mdl-column-rhs{border-left:1px solid #ccd6dd;background:#fff}
html.dark .s-minimal .mdl-header{border-bottom:1px solid #e1e8ed}
html.dark .lst-launcher .top-row{border-bottom:1px solid #e1e8ed}
html.dark .lst-launcher .is-disabled a,html.dark .lst-launcher .is-disabled a:hover,html.dark .lst-launcher .is-disabled a:focus,html.dark .lst-launcher .is-disabled a:active{background:#fff}
html.dark .lst-launcher a:hover,html.dark .lst-launcher a:focus,html.dark .lst-launcher a:active{background:#f2f9ff;border:1px solid #bbddf5}
html.dark .lst-profile a,html.dark .lst-profile a:hover,html.dark .lst-profile a:focus,html.dark .lst-profile a:active{color:#657786}
html.dark .mdl-col-settings{background-color:#fff;border-left:1px solid #E1E8ED}
html.dark .mdl-links{color:#8899a6}
html.dark .mdl-links a{color:#8899a6}
html.dark .mdl-account-shared-warning .mdl-content{background:#fff}
html.dark .char-count:disabled{color:#777}
html.dark .over-char-count:disabled{color:#be1931}
html.dark .cmp-replyto{background-color:#eaeaea;border-top:1px solid #ddd}
html.dark .s-link-added.s-photo-added p:last-child{border-top:1px solid #ddd}
html.dark .accs li{background:#eaeaea;border:1px solid #e1e8ed}
html.dark .accs li:hover{background:#e1e8ed}
html.dark .accs .icon,html.dark .accs .Icon{color:#999}
html.dark .accs .acc-selected{background-color:#55acee;border:1px solid #e1e8ed}
html.dark .accs .acc-selected i{color:#fff}
html.dark .accs .acc-selected:hover{border-color:#e1e8ed;background-color:#50a5e6}
html.dark .inline-reply{background-color:#485865;color:#fff}
html.dark .inline-reply .btn-neutral,html.dark .inline-reply .character-count{color:#fff}
html.dark .reply-triangle{border-color:transparent transparent #485865}
html.dark .detail-view-inline-text{border:1px solid #ccd6dd;background-color:#fff;color:#8899a6}
html.dark .is-inverted-dark .detail-view-inline{border-color:#ccd6dd}
html.dark .med-fullpanel{background-color:#111}
html.dark .med-link{color:#8bd}
html.dark .med-origlink,html.dark .med-flaglink{color:#8bd}
html.dark .med-origlink:hover,html.dark .med-flaglink:hover{color:#8bd}
html.dark .embed-modal .mdl-content{background:#fff}
html.dark .embed-loading-container{border:1px solid #ccd6dd}
html.dark .keyboard-shortcut-list-modal .mdl-content{background:#fff}
html.dark .text-like-keyboard-key{background-color:#eaeaea;border:1px solid #e1e8ed;box-shadow:0 1px 2px #e1e8ed,0 1px 2px #fff inset}
html.dark .s-checked .checked{color:#5c913b}
html.dark .list-link,html.dark .list-twitter-list,html.dark .list-subtitle,html.dark .list-account,html.dark .list-listmember,html.dark .list-account,html.dark .list-listaccount,html.dark .list-subtitle,html.dark .list-filter{color:#292F33}
html.dark .list-link:hover,html.dark .list-twitter-list:hover,html.dark .list-subtitle:hover,html.dark .list-account:hover,html.dark .list-listmember:hover,html.dark .list-account:hover,html.dark .list-listaccount:hover,html.dark .list-subtitle:hover{color:#111;background:#fff}
html.dark .list-link:hover:hover,html.dark .list-twitter-list:hover:hover,html.dark .list-subtitle:hover:hover,html.dark .list-account:hover:hover,html.dark .list-listmember:hover:hover,html.dark .list-link:hover:focus,html.dark .list-twitter-list:hover:focus,html.dark .list-subtitle:hover:focus,html.dark .list-account:hover:focus,html.dark .list-listmember:hover:focus,html.dark .list-link:hover:active,html.dark .list-twitter-list:hover:active,html.dark .list-subtitle:hover:active,html.dark .list-account:hover:active,html.dark .list-listmember:hover:active,html.dark .list-account:hover:hover,html.dark .list-account:hover:focus,html.dark .list-account:hover:active,html.dark .list-listaccount:hover:hover,html.dark .list-listaccount:hover:focus,html.dark .list-listaccount:hover:active,html.dark .list-subtitle:hover:hover,html.dark .list-subtitle:hover:focus,html.dark .list-subtitle:hover:active{color:#111;background:#fff}
html.dark .list-twitter-list .inner strong{color:#292F33}
html.dark .list-twitter-list .bytext,html.dark .list-twitter-list .txt-ellipsis{color:#8899a6}
html.dark .list-twitter-list .subtitle{color:#8899a6}
html.dark .list-subtitle span{color:#8899a6}
html.dark .list-account{text-shadow:0 1px 0 #fff}
html.dark .list-account .fullname{color:#292F33}
html.dark .list-account .username{color:#8899a6}
html.dark .list-listmember .username{color:#8899a6}
html.dark .list-listmember .bio{color:#657786}
html.dark .divider-bar{background-color:#ddd}
html.dark select{background-image:url("data:image/svg+xml;utf8,<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='18' height='8' viewBox='0 0 18 8'><path fill='#aaa' d='M9.82875,0.840168025 C9.59875,0.608328018 9.22625,0.608328018 8.99625,0.840168025 L5.00125,4.86964815 L1.00375,0.840168025 C0.77375,0.608328018 0.40125,0.608328018 0.17125,0.840168025 C-0.05875,1.07200803 -0.05625,1.44748804 0.17125,1.67932805 L4.58375,6.12712819 C4.69875,6.24304819 4.84875,6.30100819 5.00125,6.30100819 C5.15125,6.30100819 5.30125,6.24304819 5.41625,6.12712819 L9.82875,1.67932805 C10.05875,1.44748804 10.05875,1.07200803 9.82875,0.840168025'></path></svg>");background-color:#fff}
html.dark input,html.dark textarea,html.dark select{color:#111;border:1px solid #e1e8ed}
html.dark input:disabled{background-color:#eaeaea;border-color:#e1e8ed}
html.dark select:disabled{background-color:#f5f8fa}
html.dark input:focus,html.dark select:focus,html.dark textarea:focus,html.dark .focus{border-color:rgba(80,165,230,0.8);box-shadow:inset 0 1px 3px rgba(17,17,17,0.1),0 0 8px rgba(80,165,230,0.6)}
html.dark input.on-blue:focus{box-shadow:0 0 2px 3px #50a5e6}
html.dark .frm{color:#111}
html.dark .with-emphasis{border:1px solid #8899a6}
html.dark .with-emphasis:disabled{border-color:#999}
html.dark::-webkit-input-placeholder input::-webkit-input-placeholder,html.dark textarea::-webkit-input-placeholder{color:#aab8c2}
html.dark::placeholder input::placeholder,html.dark textarea::placeholder{color:#aab8c2}
html.dark::-webkit-validation-bubble-message{border:1px solid #ea596e;background-color:#ffe8eb}
html.dark::-webkit-validation-bubble-arrow{border:1px solid #ea596e;background-color:#ffe8eb}
html.dark .s-error input{border-color:rgba(200,120,114,0.8)}
html.dark .s-error label{color:#be1931}
html.dark .s-error input:focus{border-color:#c87872;box-shadow:0 0 6px rgba(200,120,114,0.5)}
html.dark input,html.dark textarea,html.dark select{color:#111}
html.dark .search-input-perform-search,html.dark .search-input-clear-search,html.dark .search-input-spinner{color:#aab8c2}
html.dark .search-input-perform-search:hover,html.dark .search-input-clear-search:hover,html.dark .search-input-spinner:hover{color:#aab8c2}
html.dark .input-clear-control{color:#aab8c2}
html.dark .input-clear-control:hover{color:#aab8c2}
html.dark .toggle-item.is-selected{color:#e1e8ed}
html.dark .toggle-item{color:#8bd}
html.dark .add-on{color:#999;border:1px solid #e1e8ed}
html.dark .add-on.with-emphasis{border:1px solid #8899a6}
html.dark .input-prepend input{border-left-color:#ccd6dd}
html.dark #calbody{background:#fff}
html.dark #caldays{border-bottom:1px solid #eaeaea}
html.dark #caldays span{color:#292f33}
html.dark #calweeks{background-color:#fff}
html.dark .calweek a{color:#444}
html.dark .calweek a:hover,html.dark .calfocus{background-color:#ccd6dd}
html.dark a.caloff{color:#ccd6dd}
html.dark a.caloff:hover{color:#fff;background-color:#e1e8ed}
html.dark a.caldisabled{background-color:#F5F8FA!important;color:#e1e8ed!important}
html.dark #calcurrent{background-color:#50a5e6;color:#292f33}
html.dark #caltoday{background-color:#ddd;color:#fff}
html.dark .cal{color:#e1e8ed}
html.dark .cal header{border-bottom:1px soild #eaeaea}
html.dark .prf-header{background:#292f33;text-shadow:0 1px 1px rgba(17,17,17,0.8);color:#fff}
html.dark .prf-header .prf-siteurl,html.dark .prf-header .prf-bio a,html.dark .prf-header .pretty-link{color:#fff}
html.dark .prf-header .prf-siteurl:hover,html.dark .prf-header .prf-bio a:hover,html.dark .prf-header .pretty-link:hover{color:#fff}
html.dark .prf-header-inner-overlay{background-image:linear-gradient(transparent 0,rgba(17,17,17,0.55) 100%)}
html.dark .prf .fullname{color:#fff}
html.dark .prf .username{color:#fff}
html.dark .prf-meta{border-top:1px solid #eaeaea;background:#fff}
html.dark .prf-stats li+li a{border-left:1px solid #eaeaea}
html.dark .prf-stats a{color:#8899a6}
html.dark .prf-stats a strong{color:#292F33}
html.dark .prf-stats a:hover,html.dark .prf-stats a:hover strong{color:#1c6399}
html.dark .prf .lst-profile span{color:#aab8c2}
html.dark .prf .lst-profile i{color:#aab8c2}
html.dark .prf .lst-profile a{border-right:1px solid #fff}
html.dark .prf .lst-profile a:hover span{color:#505060}
html.dark .prf .lst-profile a:hover i{color:#505060}
html.dark .detail-group{border-bottom:#292F33}
html.dark .prf-follow-status{background-color:rgba(17,17,17,0.25);color:#fff}
html.dark .profile-full-follow-status{background-color:#eaeaea}
html.dark .social-proof-container{background-color:#e1e8ed}
html.dark .profile-full{background-color:#fff}
html.dark .profile-icon{color:#8899a6}
html.dark .profile-full-avatar{background-color:#fff}
html.dark .profile-full-bio-count{color:#292f33}
html.dark .profile-full{background-color:#111}
html.dark .profile-icon{color:#ccd6dd}
html.dark .profile-full-avatar{background-color:#111}
html.dark .profile-full-bio-count{color:#ccd6dd}
html.dark .is-inverted-dark .profile-full{background-color:#fff}
html.dark .is-inverted-dark .profile-icon{color:#8899a6}
html.dark .is-inverted-dark .profile-full-avatar{background-color:#fff}
html.dark .is-inverted-dark .profile-full-bio-count{color:#292f33}
html.dark .lst li{border-bottom:1px solid #ddd}
html.dark .lst-modal{background:#fff;border:1px solid #eaeaea}
html.dark .lst .s-selected{background-color:#50a5e6;color:#fff}
html.dark .lst .s-selected .fullname,html.dark .lst .s-selected .username{color:#fff}
html.dark .lst-group .selected{background:#55acee;color:#F5F8FA}
html.dark .lst-group .selected a:hover{background:#55acee}
html.dark .lst-group .selected .fullname,html.dark .lst-group .selected .inner strong,html.dark .lst-group .selected .list-link,html.dark .lst-group .selected .list-twitter-list,html.dark .lst-group .selected .list-subtitle,html.dark .lst-group .selected .list-account,html.dark .lst-group .selected .list-listmember,html.dark .lst-group .selected .txt-ellipsis{color:#F5F8FA}
html.dark .lst-group .selected .username,html.dark .lst-group .selected .bytext,html.dark .lst-group .selected .subtitle,html.dark .lst-group .selected .icon-protected{color:#eef3f7}
html.dark .itm-remove{border-top:1px solid #ddd}
html.dark .caret-outer{border-bottom:7px solid rgba(17,17,17,0.1)}
html.dark .caret-inner{border-bottom:6px solid #fff}
html.dark .drp-h-divider{border-bottom:1px solid #ddd}
html.dark .dropdown-menu .typeahead-item,html.dark .dropdown-menu [data-action]{color:#292F33}
html.dark .dropdown-menu .is-selected{background:#55acee;color:#fff}
html.dark .dropdown-menu .is-selected [data-action]{color:#fff}
html.dark .dropdown-menu .is-selected a:not(:hover):not(:focus){color:#fff}
html.dark .dropdown-menu a:not(:hover):not(:focus){color:#292F33}
html.dark .dropdown-menu-old li:hover{background:#55acee}
html.dark .dropdown-menu-old li:hover a{color:#fff}
html.dark .dropdown-menu-old li:hover .attribution{color:#fff}
html.dark .non-selectable-item{color:#292F33}
html.dark .update-available-item:before{background-color:#FFAD1F}
html.dark .is-selected .update-available-item:before{background-color:rgba(41,47,51,0.2)}
html.dark .popover{background-color:#fff;box-shadow:0 0 10px rgba(17,17,17,0.7)}
html.dark .release-notes{background-color:#F5F8FA}
html.dark .release-notes-header-subtitle{color:#8899a6}
html.dark .release-notes-image-bullet{border:1px solid #ddd}
html.dark .startflow-background:before{background-color:#292f33;background-image:linear-gradient(36deg,#3b94d9 0%,transparent 100%)}
html.dark .startflow-link{color:#2b7bb9}
html.dark .startflow-link:hover,html.dark .startflow-link:focus,html.dark .startflow-link:active{color:#2b7bb9}
html.dark .startflow-link-on-background{color:#55acee}
html.dark .app-info-title{color:#fff}
html.dark .app-info-text p{color:#ccd6dd}
html.dark .form-legend{border-bottom:1px solid #ccd6dd;color:#111}
html.dark .startflow-panel,html.dark .startflow-panel-rounded{background-color:#fff;color:#292f33;border:1px solid #292f33}
html.dark .form-login-pwd::-webkit-input-placeholder,html.dark .form-login-email::-webkit-input-placeholder,html.dark .form-login-username::-webkit-input-placeholder{color:#999}
html.dark .form-login-pwd::placeholder,html.dark .form-login-email::placeholder,html.dark .form-login-username::placeholder{color:#999}
html.dark .privacy-info{color:#aab8c2}
html.dark .privacy-info a,html.dark .privacy-info a:visited,html.dark .privacy-info a:hover,html.dark .privacy-info a:active{color:#aab8c2}
html.dark .form-message{color:#fff}
html.dark .form-error-message{background-color:#a0041e}
html.dark .form-success-message{background-color:#5c913b}
html.dark .form-warning-message{background-color:#5c913b}
html.dark .startflow-msg-header{background-color:#ccd6dd}
html.dark .startflow-msg-warning{background-color:#ffcc4d}
html.dark .compose{background-color:#485865;color:#fff}
html.dark .compose-header{border-bottom:1px solid #66757f}
html.dark .compose-text-container{background-color:#fff}
html.dark .compose-text{color:#111}
html.dark .compose-text::-webkit-input-placeholder{color:#AAB8C2}
html.dark .compose-text::placeholder{color:#AAB8C2}
html.dark .compose-text-title{color:#88c9f9;color:#fff}
html.dark .compose-send-button-success{color:#fff}
html.dark .compose-reply-tweet{background-color:#e1e8ed;color:#292f33}
html.dark .compose-reply-tweet-remove{color:#292f33}
html.dark .compose-reply-tweet .tweet-body a{color:#2b7bb9}
html.dark .compose-reply-tweet .fullname{color:#292f33}
html.dark .compose-reply-tweet .username{color:#8899a6}
html.dark .replyto-caret{border-color:transparent transparent #fff}
html.dark .compose-message-account{color:#111}
html.dark .compose-message-recipient{border:1px solid #eaeaea}
html.dark .compose-message-recipient-input-container.is-focused{box-shadow:0 0 2px 3px #50a5e6}
html.dark .compose-media-bar-holder{background-color:#fff}
html.dark .compose-media-info-bar-holder{background-color:#fff;color:#8899a6}
html.dark .compose-media-info-bar{background:#e1e8ed}
html.dark .compose-account{color:#fff}
html.dark .compose-account-img{background-color:#66757f}
html.dark .compose-account:hover{color:#fff}
html.dark .compose-account:focus{color:#fff}
html.dark .compose-account:focus .compose-account-img{box-shadow:0 0 2px 3px #50a5e6}
html.dark .is-selected.compose-account:focus .compose-account-img{box-shadow:0 0 2px 3px #50a5e6}
html.dark .compose-account-selected{background-color:#17BF63}
html.dark .compose-remember-state{color:#fff}
html.dark .video-container .video-controls{background:rgba(0,0,0,0.5);background:linear-gradient(transparent,rgba(0,0,0,0.65))}
html.dark .column-nav-link:focus,html.dark .column-nav-link:active{color:#F5F8FA}
html.dark .column-nav-link.is-selected,html.dark .column-nav-link:hover{color:#fff}
html.dark .column-nav-item{color:#e1e8ed;background-color:#292f33}
html.dark .column-nav-link:after{color:#8899A6}
html.dark .column-nav-link .attribution{color:#8899A6}
html.dark .draggable-dragging{box-shadow:0 4px 10px rgba(17,17,17,0.8)}
html.dark .nav-user-info .username{color:#8899A6}
html.dark .nav-user-info .fullname{color:#F5F8FA}
html.dark .account-bio{color:#8899a6}
html.dark .DatePickerDropdown-menuItem--footer{border-top:1px solid #ccd6dd;background-color:#f5f8fa}
html.dark .DatePicker-monthButton{color:#1da1f2}
html.dark .DatePicker-monthButton:hover,html.dark .DatePicker-monthButton:focus{color:#005fd1}
html.dark .DatePicker-monthButton[disabled]{color:#ccd6dd}
html.dark .DatePicker-calendarDayHeader{color:#657786}
html.dark .DatePicker-calendarDay{color:#ccd6dd}
html.dark .DatePicker-calendarDay.is-selectable{color:#14171a}
html.dark .DatePicker-calendarDay.is-selectable.is-adjacentMonth{color:#657786}
html.dark .DatePicker-calendarDay.is-selectable:hover{background-color:#005fd1}
html.dark .DatePicker-calendarDay.is-withinRange{background-color:#1da1f2}
html.dark .DatePicker-calendarDay.is-withinRange.is-adjacentMonth{color:#ccd6dd}
html.dark .DatePicker-calendarDay.is-rangeStart,html.dark .DatePicker-calendarDay.is-rangeEnd{background-color:#005fd1}
html.dark .DatePicker-calendarDay.is-rangeStart.is-adjacentMonth,html.dark .DatePicker-calendarDay.is-rangeEnd.is-adjacentMonth{color:#ccd6dd}
html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-withinRange,html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeStart,html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeEnd{border:1px solid #1da1f2;color:#14171a}
html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-withinRange.is-adjacentMonth,html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeStart.is-adjacentMonth,html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeEnd.is-adjacentMonth{color:#657786}
html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeStart,html.dark .DatePicker--withPendingRange .DatePicker-calendarDay.is-rangeEnd{background-color:#eaf5fd}
html.dark .DatePicker-time{border-top:1px solid #ccd6dd}
html.dark .DatePicker-timeZone{color:#657786}
html.dark .Dropdown{background-color:rgba(255,255,255,0.98);box-shadow:0 1px 4px rgba(0,0,0,0.25)}
html.dark .Dropdown-detailPanel{border:1px solid #ccd6dd;background-color:#f5f8fa}
html.dark .Dropdown-divider{background-color:#ccd6dd}
html.dark .Dropdown-menuItem .Dropdown-menuItemContent,html.dark .Dropdown-menuGroupLabel{color:#14171a}
html.dark .Dropdown-menuItem .Dropdown-menuItemContent .Icon--check{color:#1da1f2}
html.dark .Dropdown-menuItem.is-focus{background-color:#1da1f2}
html.dark .Dropdown-menuItem.is-focus .User .Icon--verified::before{color:#1da1f2}
html.dark .Dropdown-menuGroupLabel{color:#657786}
html.dark .ButtonGroup>.Button.is-selected,html.dark .ButtonGroup>.Button.is-selected:visited{background-color:#1da1f2;border:1px solid #1da1f2}
html.dark .ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup>.Button.is-selected.is-hover{background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup>.Button.is-selected{background-color:#1da1f2;border-color:#1da1f2}
html.dark .ButtonGroup--tertiary>.Button.is-selected,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected,html.dark .ButtonGroup--tertiary>.Button.is-selected:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:visited{background-color:#657786;border:1px solid #657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:focus,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ccd6dd;background:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:hover,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-hover{background-color:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected:active,html.dark .ButtonGroup--tertiary>.Button.is-selected.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #aab8c2;background-color:#657786;border-color:#657786}
html.dark .ButtonGroup--tertiary>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected[disabled],html.dark .ButtonGroup--tertiary>.Button.is-selected.is-disabled,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button.is-selected,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button.is-selected{background-color:#657786;border-color:#657786}
html.dark .Button,html.dark .Button:visited,html.dark .Button.is-visited{border:1px solid #1da1f2;color:#1da1f2}
html.dark .Button:focus,html.dark .Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;border-color:#1da1f2;color:#1da1f2}
html.dark .Button:hover,html.dark .Button.is-hover{background-color:#eaf5fd;color:#1da1f2}
html.dark .Button:active,html.dark .Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background:#eaf5fd;border-color:#1da1f2;color:#1da1f2}
html.dark .Button.Button--primary,html.dark .Button.Button--primary:visited,html.dark .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.Button:visited,html.dark .ButtonGroup--primary>.ButtonGroup>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:visited{background-color:#1da1f2;border:1px solid #1da1f2}
html.dark .Button.Button--primary:focus,html.dark .Button.Button--primary.is-focus,html.dark .ButtonGroup--primary>.Button:focus,html.dark .ButtonGroup--primary>.Button.is-focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8;background:#1da1f2;border-color:#1da1f2}
html.dark .Button.Button--primary:hover,html.dark .Button.Button--primary.is-hover,html.dark .ButtonGroup--primary>.Button:hover,html.dark .ButtonGroup--primary>.Button.is-hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-hover{background-color:#005fd1;border-color:#005fd1}
html.dark .Button.Button--primary:active,html.dark .Button.Button--primary.is-active,html.dark .ButtonGroup--primary>.Button:active,html.dark .ButtonGroup--primary>.Button.is-active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2;background-color:#005fd1;border-color:#005fd1}
html.dark .Button.Button--primary[disabled],html.dark .Button.Button--primary.is-disabled,html.dark fieldset[disabled] .Button.Button--primary,html.dark .ButtonGroup--primary>.Button[disabled],html.dark .ButtonGroup--primary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.Button,html.dark .ButtonGroup--primary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--primary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--primary>.ButtonGroup>.Button{background-color:#1da1f2;border-color:#1da1f2}
html.dark .Button.Button--tertiary,html.dark .Button.Button--tertiary:visited,html.dark .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.Button:visited,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:visited{border:1px solid #657786;color:#657786}
html.dark .Button.Button--tertiary:focus,html.dark .Button.Button--tertiary.is-focus,html.dark .ButtonGroup--tertiary>.Button:focus,html.dark .ButtonGroup--tertiary>.Button.is-focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:focus,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ccd6dd;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary:hover,html.dark .Button.Button--tertiary.is-hover,html.dark .ButtonGroup--tertiary>.Button:hover,html.dark .ButtonGroup--tertiary>.Button.is-hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:hover,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-hover{background-color:#f5f8fa;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary:active,html.dark .Button.Button--tertiary.is-active,html.dark .ButtonGroup--tertiary>.Button:active,html.dark .ButtonGroup--tertiary>.Button.is-active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button:active,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #657786;background-color:#f5f8fa;border-color:#657786;color:#657786}
html.dark .Button.Button--tertiary[disabled],html.dark .Button.Button--tertiary.is-disabled,html.dark fieldset[disabled] .Button.Button--tertiary,html.dark .ButtonGroup--tertiary>.Button[disabled],html.dark .ButtonGroup--tertiary>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.Button,html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button[disabled],html.dark .ButtonGroup--tertiary>.ButtonGroup>.Button.is-disabled,html.dark fieldset[disabled] .ButtonGroup--tertiary>.ButtonGroup>.Button{border-color:#657786}
html.dark .Button.Button--success,html.dark .Button.Button--success:visited{background-color:#17bf63;border:1px solid #17bf63}
html.dark .Button.Button--success:focus,html.dark .Button.Button--success.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #68e090;background:#17bf63;border-color:#17bf63}
html.dark .Button.Button--success:hover,html.dark .Button.Button--success.is-hover{background-color:#008951;border-color:#008951}
html.dark .Button.Button--success:active,html.dark .Button.Button--success.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #17bf63;background-color:#008951;border-color:#008951}
html.dark .Button.Button--success[disabled],html.dark .Button.Button--success.is-disabled,html.dark fieldset[disabled] .Button.Button--success{background-color:#17bf63;border-color:#17bf63}
html.dark .Button.Button--warning,html.dark .Button.Button--warning:visited{background-color:#ffad1f;border:1px solid #ffad1f}
html.dark .Button.Button--warning:focus,html.dark .Button.Button--warning.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #ffd03f;background:#ffad1f;border-color:#ffad1f}
html.dark .Button.Button--warning:hover,html.dark .Button.Button--warning.is-hover{background-color:#f98e00;border-color:#f98e00}
html.dark .Button.Button--warning:active,html.dark .Button.Button--warning.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #ffad1f;background-color:#f98e00;border-color:#f98e00}
html.dark .Button.Button--warning[disabled],html.dark .Button.Button--warning.is-disabled,html.dark fieldset[disabled] .Button.Button--warning{background-color:#ffad1f;border-color:#ffad1f}
html.dark .Button.Button--danger,html.dark .Button.Button--danger:visited{background-color:#e0245e;border:1px solid #e0245e}
html.dark .Button.Button--danger:focus,html.dark .Button.Button--danger.is-focus{box-shadow:0 0 0 2px white,0 0 0 4px #f6809a;background:#e0245e;border-color:#e0245e}
html.dark .Button.Button--danger:hover,html.dark .Button.Button--danger.is-hover{background-color:#a01744;border-color:#a01744}
html.dark .Button.Button--danger:active,html.dark .Button.Button--danger.is-active{box-shadow:0 0 0 2px white,0 0 0 4px #e0245e;background-color:#a01744;border-color:#a01744}
html.dark .Button.Button--danger[disabled],html.dark .Button.Button--danger.is-disabled,html.dark fieldset[disabled] .Button.Button--danger{background-color:#e0245e;border-color:#e0245e}
html.dark .Button.Button--link{color:#1b95e0}
html.dark .Button.Button--dangerLink{color:#e0245e}
html.dark .ProgressBar{background-color:#ccd6dd;color:#1da1f2}
html.dark .ProgressBar::-webkit-progress-bar{background-color:#ccd6dd}
html.dark .ProgressBar.ProgressBar:indeterminate{border-top:1.5px solid #ccd6dd;border-bottom:1.5px solid #ccd6dd}
html.dark .ProgressBar::-webkit-progress-value{background-color:#1da1f2}
html.dark .ProgressBar--red{color:#e0245e}
html.dark .ProgressBar--red::-webkit-progress-value{background-color:#e0245e}
html.dark .ProgressBar--yellow{color:#ffad1f}
html.dark .ProgressBar--yellow::-webkit-progress-value{background-color:#ffad1f}
html.dark .ProgressBar--green{color:#17bf63}
html.dark .ProgressBar--green::-webkit-progress-value{background-color:#17bf63}
html.dark .ProgressBar--blue{color:#1da1f2}
html.dark .ProgressBar--blue::-webkit-progress-value{background-color:#1da1f2}
html.dark .ProgressBar--white{background-color:#657786}
html.dark .ProgressBar--white::-webkit-progress-bar{background-color:#657786}
html.dark .ProgressBar--large.ProgressBar:indeterminate{border-top:4.5px solid #ccd6dd;border-bottom:4.5px solid #ccd6dd}
html.dark .Notification-inner{box-shadow:0 2px 4px rgba(0,0,0,0.1)}
html.dark .Notification-icon{background-color:#1da1f2}
html.dark .Notification-content{border:1px solid #ccd6dd}
html.dark .Notification-title+.Notification-body{color:#657786}
html.dark .Notification-closeButton{color:#aab8c2}
html.dark .Notification-closeButton:hover,html.dark .Notification-closeButton:focus{color:#657786}
html.dark .Notification--green .Notification-icon{background-color:#17bf63}
html.dark .Notification--red .Notification-icon{background-color:#e0245e}
html.dark .ModalOverlay{background-color:rgba(20,23,26,0.8)}
html.dark .Drawer{background-color:#fff}
html.dark .Drawer:not([dir="rtl"]){border-left:1px solid #ccd6dd}
html.dark .Drawer[dir="rtl"]{border-right:1px solid #ccd6dd}
html.dark .Drawer--modal{background-color:#fff}
html.dark .Drawer-close{color:#aab8c2}
html.dark .Drawer-close:hover{color:#657786}
html.dark .DialogContent-title{border-bottom:2px solid #ccd6dd}
html.dark .DialogContent-footer{background-color:#f5f8fa;border-top:1px solid #ccd6dd}
html.dark .Tooltip{border-color:#ccd6dd;box-shadow:0 2px 4px rgba(0,0,0,0.1)}
html.dark .Tooltip .Tooltip-content{color:#14171a}
html.dark .Tooltip .Tooltip-close{color:#aab8c2}
html.dark .Tooltip .Tooltip-triangleOuter{border-color:transparent #ccd6dd transparent transparent}
html.dark .Tooltip.Tooltip--left .Tooltip-triangleOuter,html.dark .Tooltip.Tooltip--topLeft .Tooltip-triangleOuter,html.dark .Tooltip.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent transparent #ccd6dd}
html.dark .Tooltip.Tooltip--top .Tooltip-triangleOuter,html.dark .Tooltip.Tooltip--topLeft .Tooltip-triangleOuter{border-color:#ccd6dd transparent transparent}
html.dark .Tooltip.Tooltip--bottom .Tooltip-triangleOuter,html.dark .Tooltip.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent #ccd6dd}
html.dark .Tooltip--dark{background:#14171a;border-color:#14171a}
html.dark .Tooltip--dark .Tooltip-triangleOuter{border-color:transparent #14171a transparent transparent}
html.dark .Tooltip--dark .Tooltip-triangleInner{border-color:transparent #14171a transparent transparent}
html.dark .Tooltip--dark.Tooltip--left .Tooltip-triangleOuter,html.dark .Tooltip--dark.Tooltip--topLeft .Tooltip-triangleOuter,html.dark .Tooltip--dark.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent transparent #14171a}
html.dark .Tooltip--dark.Tooltip--left .Tooltip-triangleInner,html.dark .Tooltip--dark.Tooltip--topLeft .Tooltip-triangleInner,html.dark .Tooltip--dark.Tooltip--bottomLeft .Tooltip-triangleInner{border-color:transparent transparent transparent #14171a}
html.dark .Tooltip--dark.Tooltip--top .Tooltip-triangleOuter,html.dark .Tooltip--dark.Tooltip--topLeft .Tooltip-triangleOuter{border-color:#14171a transparent transparent}
html.dark .Tooltip--dark.Tooltip--top .Tooltip-triangleInner,html.dark .Tooltip--dark.Tooltip--topLeft .Tooltip-triangleInner{border-color:#14171a transparent transparent}
html.dark .Tooltip--dark.Tooltip--bottom .Tooltip-triangleOuter,html.dark .Tooltip--dark.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent #14171a}
html.dark .Tooltip--dark.Tooltip--bottom .Tooltip-triangleInner,html.dark .Tooltip--dark.Tooltip--bottomLeft .Tooltip-triangleInner{border-color:transparent transparent #14171a}
html.dark .Tooltip--intro{background:#1da1f2;border-color:#1da1f2}
html.dark .Tooltip--intro .Tooltip-triangleOuter{border-color:transparent #1da1f2 transparent transparent}
html.dark .Tooltip--intro .Tooltip-triangleInner{border-color:transparent #1da1f2 transparent transparent}
html.dark .Tooltip--intro.Tooltip--left .Tooltip-triangleOuter,html.dark .Tooltip--intro.Tooltip--topLeft .Tooltip-triangleOuter,html.dark .Tooltip--intro.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent transparent #1da1f2}
html.dark .Tooltip--intro.Tooltip--left .Tooltip-triangleInner,html.dark .Tooltip--intro.Tooltip--topLeft .Tooltip-triangleInner,html.dark .Tooltip--intro.Tooltip--bottomLeft .Tooltip-triangleInner{border-color:transparent transparent transparent #1da1f2}
html.dark .Tooltip--intro.Tooltip--top .Tooltip-triangleOuter,html.dark .Tooltip--intro.Tooltip--topLeft .Tooltip-triangleOuter{border-color:#1da1f2 transparent transparent}
html.dark .Tooltip--intro.Tooltip--top .Tooltip-triangleInner,html.dark .Tooltip--intro.Tooltip--topLeft .Tooltip-triangleInner{border-color:#1da1f2 transparent transparent}
html.dark .Tooltip--intro.Tooltip--bottom .Tooltip-triangleOuter,html.dark .Tooltip--intro.Tooltip--bottomLeft .Tooltip-triangleOuter{border-color:transparent transparent #1da1f2}
html.dark .Tooltip--intro.Tooltip--bottom .Tooltip-triangleInner,html.dark .Tooltip--intro.Tooltip--bottomLeft .Tooltip-triangleInner{border-color:transparent transparent #1da1f2}
html.dark .Tooltip-close:hover{color:#657786}
html.dark .TooltipHoverTarget{background-image:linear-gradient(to right,#1da1f2 50%,transparent 0%)}
html.dark .LegendItem-color{background-color:#aab8c2}
html.dark .LegendItem--gray .LegendItem-color{background-color:#aab8c2}
html.dark .LegendItem--blue .LegendItem-color{background-color:#1da1f2}
html.dark .LegendItem--green .LegendItem-color{background-color:#17bf63}
html.dark .LegendItem--yellow .LegendItem-color{background-color:#ffad1f}
html.dark .LegendItem--red .LegendItem-color{background-color:#e0245e}
html.dark .LegendItem--purple .LegendItem-color{background-color:#794bc4}
html.dark .DateRangeDropdown-menuItem--footer{border-top:1px solid #ccd6dd;background-color:#f5f8fa}
html.dark .DateRange:not([dir='rtl']) .DateRange-presets{border-right:1px solid #ccd6dd}
html.dark .DateRange:not([dir='rtl']) .DateRange-pickersRow:first-child .DateRange-pickerWrapper:last-child{border-left:1px solid #ccd6dd}
html.dark .DateRange[dir='rtl'] .DateRange-presets{border-left:1px solid #ccd6dd}
html.dark .DateRange[dir='rtl'] .DateRange-pickersRow:first-child .DateRange-pickerWrapper:last-child{border-right:1px solid #ccd6dd}
html.dark .PillGroup .Pill.is-selected{background:#005fd1}
html.dark .PillGroup .Pill>a,html.dark .PillGroup .Pill>button{color:#1b95e0}
html.dark .PillGroup .Pill>a:hover,html.dark .PillGroup .Pill>button:hover{background:#eaf5fd}
html.dark .PillGroup .Pill>a:focus,html.dark .PillGroup .Pill>button:focus{box-shadow:0 0 0 2px white,0 0 0 4px #71c9f8}
html.dark .PillGroup .Pill>a:active,html.dark .PillGroup .Pill>button:active{box-shadow:0 0 0 2px white,0 0 0 4px #1da1f2}
html.dark .PillGroup .Pill.is-selected>a,html.dark .PillGroup .Pill.is-selected>button{color:#FFF}
html.dark .FormInput,html.dark .FormTextarea{border:1px solid #ccd6dd;color:#14171a}
html.dark .FormInput-characterCount{color:#ccd6dd}
html.dark .FormInput-characterCount.is-negative{color:#e0245e}
html.dark .FormInput::-webkit-input-placeholder,html.dark .FormTextarea::-webkit-input-placeholder{color:#aab8c2}
html.dark .FormInput[disabled],html.dark .FormTextarea[disabled],html.dark .FormInput.is-disabled,html.dark .FormTextarea.is-disabled,html.dark fieldset[disabled] .FormInput,html.dark fieldset[disabled] .FormTextarea,html.dark .FormInputWrapper.is-disabled .FormInput{background:#f5f8fa;color:#657786}
html.dark .FormInput.is-error,html.dark .FormTextarea.is-error,html.dark .FormInput.is-invalid,html.dark .FormTextarea.is-invalid,html.dark .FormInputWrapper.is-invalid .FormInput{border-color:#e0245e}
html.dark .FormInput.is-error:focus,html.dark .FormTextarea.is-error:focus,html.dark .FormInput.is-invalid:focus,html.dark .FormTextarea.is-invalid:focus,html.dark .FormInput.is-error.is-focus,html.dark .FormTextarea.is-error.is-focus,html.dark .FormInput.is-invalid.is-focus,html.dark .FormTextarea.is-invalid.is-focus,html.dark .FormInputWrapper.is-invalid .FormInput:focus,html.dark .FormInputWrapper.is-invalid .FormInput.is-focus{border-color:#e0245e;box-shadow:inset 0 0 0 1px #e0245e}
html.dark .FormInput.is-valid,html.dark .FormTextarea.is-valid{border-color:#17bf63}
html.dark .FormInput.is-valid:focus,html.dark .FormTextarea.is-valid:focus,html.dark .FormInput.is-valid.is-focus,html.dark .FormTextarea.is-valid.is-focus{border-color:#17bf63;box-shadow:inset 0 0 0 1px #17bf63}
html.dark .FormInput:focus,html.dark .FormTextarea:focus,html.dark .FormInput.is-focus,html.dark .FormTextarea.is-focus,html.dark .FormInputWrapper.is-focus .FormInput{border-color:#1da1f2;box-shadow:inset 0 0 0 1px #1da1f2}
html.dark .FormOption.is-disabled{color:#aab8c2}
html.dark .FormInputWrapper-absoluteStartAdornment .Icon,html.dark .FormInputWrapper-absoluteEndAdornment .Icon{color:#aab8c2}
html.dark .FormInputWrapper-absoluteStartAdornment .Icon--caretDown,html.dark .FormInputWrapper-absoluteEndAdornment .Icon--caretDown{color:#14171a}
html.dark .FormInputWrapper.is-disabled .FormInputWrapper-absoluteStartAdornment .Icon--caretDown,html.dark .FormInputWrapper.is-disabled .FormInputWrapper-absoluteEndAdornment .Icon--caretDown,html.dark fieldset[disabled] .FormInputWrapper-absoluteStartAdornment .Icon--caretDown,html.dark fieldset[disabled] .FormInputWrapper-absoluteEndAdornment .Icon--caretDown{color:#657786}
html.dark .FormInputWrapper-startAdornment,html.dark .FormInputWrapper-endAdornment{border:1px solid #ccd6dd}
html.dark .FormField.is-invalid .FormField-validationMessage{color:#e0245e}
html.dark .FormField.is-valid .FormField-validationMessage{color:#008951}
html.dark .FormField-description{color:#657786}
html.dark .Token-checkbox input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 5px white,0 0 0 7px #71c9f8}
html.dark .Token--small .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--small>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 3px white,0 0 0 5px #71c9f8}
html.dark .Token--xsmall .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.Token .Token-trigger input[type="checkbox"]:focus+.Icon,html.dark .TokenGroup--xsmall>.TokenGroup>.Token .Token-trigger input[type="checkbox"]:focus+.Icon{box-shadow:0 0 0 0 white,0 0 0 2px #71c9f8}
html.dark .Token,html.dark .Token--blue{border-color:#1da1f2;color:#1da1f2}
html.dark .Token .Token-adornment,html.dark .Token--blue .Token-adornment{background-color:#1da1f2}
html.dark .Token:hover,html.dark .Token--blue:hover{background-color:#97e3ff;color:#005fd1}
html.dark .Token.is-selected,html.dark .Token--blue.is-selected{background-color:#1da1f2}
html.dark .Token.is-selected .Token-adornment,html.dark .Token--blue.is-selected .Token-adornment{color:#1da1f2}
html.dark .Token.is-selected:hover,html.dark .Token--blue.is-selected:hover{background-color:#005fd1;border-color:#005fd1}
html.dark .Token:focus,html.dark .Token--blue:focus,html.dark .Token.is-focused,html.dark .Token--blue.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #71c9f8}
html.dark .Token--green{border-color:#17bf63;color:#17bf63}
html.dark .Token--green .Token-adornment{background-color:#17bf63}
html.dark .Token--green:hover{background-color:#a5f2aa;color:#008951}
html.dark .Token--green.is-selected{background-color:#17bf63}
html.dark .Token--green.is-selected .Token-adornment{color:#17bf63}
html.dark .Token--green.is-selected:hover{background-color:#008951;border-color:#008951}
html.dark .Token--green:focus,html.dark .Token--green.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #68e090}
html.dark .Token--red{border-color:#e0245e;color:#e0245e}
html.dark .Token--red .Token-adornment{background-color:#e0245e}
html.dark .Token--red:hover{background-color:#ffb8c2;color:#a01744}
html.dark .Token--red.is-selected{background-color:#e0245e}
html.dark .Token--red.is-selected .Token-adornment{color:#e0245e}
html.dark .Token--red.is-selected:hover{background-color:#a01744;border-color:#a01744}
html.dark .Token--red:focus,html.dark .Token--red.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #f6809a}
html.dark .Token--purple{border-color:#794bc4;color:#794bc4}
html.dark .Token--purple .Token-adornment{background-color:#794bc4}
html.dark .Token--purple:hover{background-color:#c7b4fa;color:#4f0299}
html.dark .Token--purple.is-selected{background-color:#794bc4}
html.dark .Token--purple.is-selected .Token-adornment{color:#794bc4}
html.dark .Token--purple.is-selected:hover{background-color:#4f0299;border-color:#4f0299}
html.dark .Token--purple:focus,html.dark .Token--purple.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #a37ced}
html.dark .Token--yellow{border-color:#ffad1f;color:#ffad1f}
html.dark .Token--yellow .Token-adornment{background-color:#ffad1f}
html.dark .Token--yellow:hover{background-color:#ffe76e;color:#f98e00}
html.dark .Token--yellow.is-selected{background-color:#ffad1f}
html.dark .Token--yellow.is-selected .Token-adornment{color:#ffad1f}
html.dark .Token--yellow.is-selected:hover{background-color:#f98e00;border-color:#f98e00}
html.dark .Token--yellow:focus,html.dark .Token--yellow.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #ffd03f}
html.dark .Token--gray{border-color:#657786;color:#657786}
html.dark .Token--gray .Token-adornment{background-color:#657786}
html.dark .Token--gray:hover{background-color:#e6ecf0;color:#657786}
html.dark .Token--gray.is-selected{background-color:#657786}
html.dark .Token--gray.is-selected .Token-adornment{color:#657786}
html.dark .Token--gray.is-selected:hover{background-color:#aab8c2;border-color:#aab8c2}
html.dark .Token--gray:focus,html.dark .Token--gray.is-focused{box-shadow:0 0 0 1px white,0 0 0 3px #aab8c2}
html.dark .FormTokenInput-input::-webkit-input-placeholder{color:#aab8c2}
html.dark .DataPoint .DataPoint-label{color:#657786}
html.dark .DataPoint .DataPoint-info{color:#14171a}
html.dark .DataPoint .DataPoint-trend--negative{color:#e0245e}
html.dark .DataPoint .DataPoint-trend--positive{color:#17bf63}
html.dark .DataPoint--withBottomBorder{border-bottom:1px solid #ccd6dd}
html.dark .FormTokenInput.FormTextarea::-webkit-input-placeholder{color:#8899A6}
html.dark .FormTokenInput.FormTextarea::placeholder{color:#8899A6}
html.dark .DatePicker.date-unselected .is-rangeStart,html.dark .DatePicker.date-unselected .is-rangeEnd{color:#14171a}
html.dark .DatePicker.date-unselected .is-rangeStart:hover,html.dark .DatePicker.date-unselected .is-rangeEnd:hover{background-color:#005091;color:#ffffff}
html.dark .NotificationList .Notification-body{color:#14171A}
html.dark .DrawerModal{color:#14171A}
/* fixes */
html.dark .app-search-fake{border-color:transparent}
html.dark .spinner-small,html.dark .spinner-large{filter:grayscale(80%)brightness(93%)}

View File

@@ -157,7 +157,6 @@
thumbSizeClass: "media-size-medium" thumbSizeClass: "media-size-medium"
})); }));
html.css("border", "0");
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
html.find(".js-quote-detail").removeClass("is-actionable margin-b--8"); // prevent quoted tweets from changing the cursor and reduce bottom margin html.find(".js-quote-detail").removeClass("is-actionable margin-b--8"); // prevent quoted tweets from changing the cursor and reduce bottom margin
@@ -186,6 +185,12 @@
return $(this).text() === "Show this thread"; return $(this).text() === "Show this thread";
}).first().each(function(){ }).first().each(function(){
this.id = "tduck-show-thread"; this.id = "tduck-show-thread";
let moveBefore = html.find(".tweet-body > .js-media, .tweet-body > .js-media-preview-container, .quoted-tweet");
if (moveBefore){
$(this).css("margin-top", "5px").removeClass("margin-b--5").parent("span").detach().insertBefore(moveBefore);
}
}); });
let type = tweet.getChirpType(); let type = tweet.getChirpType();
@@ -194,6 +199,9 @@
html.find(".js-user-actions-menu").parent().remove(); html.find(".js-user-actions-menu").parent().remove();
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px"); html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
} }
else if (type.startsWith("favorite") || type.startsWith("retweet")){
html.children().first().addClass("td-notification-padded");
}
else if (type.includes("list_member")){ else if (type.includes("list_member")){
html.find(".activity-header").css("margin-top", "2px"); html.find(".activity-header").css("margin-top", "2px");
html.find(".avatar").first().css("margin-bottom", "0"); html.find(".avatar").first().css("margin-bottom", "0");
@@ -280,7 +288,7 @@
"<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>" "<html "+Array.prototype.map.call(document.documentElement.attributes, ele => `${ele.name}="${ele.value}"`).join(" ")+"><head>"
]; ];
$(document.head).children("link[href*='css/font.']:first,link[href*='css/app-"+themeName+".']:first,meta[charset],meta[http-equiv]").each(function(){ $(document.head).children("link[rel='stylesheet'],meta[charset]").each(function(){
tags.push($(this)[0].outerHTML); tags.push($(this)[0].outerHTML);
}); });
@@ -289,9 +297,9 @@
tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls tags.push("a[data-full-url] { word-break: break-all !important }"); // break long urls
tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media tags.push(".media-item, .media-preview { border-radius: 1px !important }"); // square-ify media
tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets tags.push(".quoted-tweet { border-radius: 0 !important }"); // square-ify quoted tweets
tags.push(".tweet-context .nbfc { text-overflow: ellipsis !important; white-space: nowrap !important }"); // force ellipsis on long usernames tags.push(".activity-header.has-source-avatar { margin-bottom: 4px !important }"); // tweak distance between avatar and text
tags.push(".activity-header { align-items: center !important; margin-bottom: 4px !important }"); // tweak alignment of avatar and text in notifications tags.push(".activity-header .tweet-timestamp { line-height: unset !important }"); // fix timestamp position
tags.push(".activity-header .tweet-timestamp { line-height: unset !important }"); // fix timestamp position in notifications tags.push(".activity-header .icon-user-filled { vertical-align: sub !important; }"); // fix follow icon position
tags.push("#tduck-show-thread { display: inline-block !important; cursor: pointer }"); tags.push("#tduck-show-thread { display: inline-block !important; cursor: pointer }");
if (fontSizeName === "smallest"){ if (fontSizeName === "smallest"){
@@ -446,21 +454,6 @@
data.setData("text/html", `<a href="${url}">${url}</a>`); data.setData("text/html", `<a href="${url}">${url}</a>`);
}); });
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
const prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
TD.services.TwitterUser.prototype.fromJSONObject = function(){
let obj = prevFunc.apply(this, arguments);
let e = arguments[0].entities;
if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
obj.url = e.url.urls[0].expanded_url;
}
return obj;
};
}
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){ if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity; const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
@@ -480,6 +473,66 @@
}; };
} }
//
// Block: Bypass t.co in user profiles and setup a top tier account bamboozle scheme.
//
(function(){
const realDisplayName = "TweetDuck";
const realAvatar = "https://ton.twimg.com/tduck/avatar";
const accountId = "957608948189880320";
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
const prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
TD.services.TwitterUser.prototype.fromJSONObject = function(){
let obj = prevFunc.apply(this, arguments);
let e = arguments[0].entities;
if (obj.id === accountId){
obj.name = realDisplayName;
obj.emojifiedName = realDisplayName;
obj.profileImageURL = realAvatar;
obj.url = "https://tweetduck.chylex.com";
obj.entities.url.urls = [{
url: obj.url,
expanded_url: obj.url,
display_url: "tweetduck.chylex.com",
indices: [ 0, 23 ]
}];
}
else if (e && e.url && e.url.urls && e.url.urls.length && e.url.urls[0].expanded_url){
obj.url = e.url.urls[0].expanded_url;
}
return obj;
};
}
if (ensurePropertyExists(TD, "services", "TwitterClient", "prototype", "typeaheadSearch")){
const prevFunc = TD.services.TwitterClient.prototype.typeaheadSearch;
TD.services.TwitterClient.prototype.typeaheadSearch = function(data, onSuccess, onError){
if (data.query && data.query.toLowerCase().endsWith("tweetduck")){
data.query = "TryMyAwesomeApp";
}
return prevFunc.call(this, data, function(result){
for(let user of result.users){
if (user.id_str === accountId){
user.name = realDisplayName;
user.profile_image_url = realAvatar;
user.profile_image_url_https = realAvatar;
break;
}
}
onSuccess.apply(this, arguments);
}, onError);
};
}
})();
// //
// Block: Include additional information in context menus. // Block: Include additional information in context menus.
// //
@@ -508,9 +561,33 @@
// Block: Hook into the notification sound effect. // Block: Hook into the notification sound effect.
// //
HTMLAudioElement.prototype.play = prependToFunction(HTMLAudioElement.prototype.play, function(){ HTMLAudioElement.prototype.play = prependToFunction(HTMLAudioElement.prototype.play, function(){
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound; return $TDX.muteNotifications;
}); });
window.TDGF_setSoundNotificationData = function(custom, volume){
let audio = document.getElementById("update-sound");
audio.volume = volume/100;
const sourceId = "tduck-custom-sound-source";
let source = document.getElementById(sourceId);
if (custom && !source){
source = document.createElement("source");
source.id = sourceId;
source.src = "https://ton.twimg.com/tduck/updatesnd";
audio.prepend(source);
}
else if (!custom && source){
audio.removeChild(source);
}
audio.load();
};
window.TDGF_playSoundNotification = function(){
document.getElementById("update-sound").play();
};
// //
// Block: Update highlighted column and tweet for context menu and other functionality. // Block: Update highlighted column and tweet for context menu and other functionality.
// //
@@ -601,7 +678,11 @@
let isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before")); let isReply = !isDetail && (parent.hasClass("js-replies-to") || parent.hasClass("js-replies-before"));
selectedTweet = selectedTweet.clone(); selectedTweet = selectedTweet.clone();
selectedTweet.children().first().addClass($(document.documentElement).attr("class")).css("padding-bottom", "0");
let container = selectedTweet.children().first();
container.addClass($(document.documentElement).attr("class"));
container.addClass($(document.body).attr("class"));
container.css("padding-bottom", "0");
setImportantProperty(selectedTweet.find(".js-tweet-text"), "margin-bottom", "8px"); setImportantProperty(selectedTweet.find(".js-tweet-text"), "margin-bottom", "8px");
setImportantProperty(selectedTweet.find(".js-quote-detail"), "margin-bottom", "10px"); setImportantProperty(selectedTweet.find(".js-quote-detail"), "margin-bottom", "10px");
@@ -621,7 +702,10 @@
selectedTweet.find("footer").last().prevUntil(":not(.txt-mute)").addBack().remove(); // footer, date, location selectedTweet.find("footer").last().prevUntil(":not(.txt-mute)").addBack().remove(); // footer, date, location
} }
else{ else{
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "10px"); setImportantProperty(selectedTweet.find(".js-media-preview-container").filter(function(){
return $(this).closest(".js-quote-detail").length === 0;
}), "margin-bottom", "10px");
selectedTweet.find("footer").last().remove(); selectedTweet.find("footer").last().remove();
} }
@@ -630,6 +714,10 @@
selectedTweet.find(".thread").remove(); selectedTweet.find(".thread").remove();
} }
selectedTweet.find("p.link-complex-target").filter(function(){
return $(this).text() === "Show this thread";
}).first().remove();
selectedTweet.find(".js-poll-link").remove(); selectedTweet.find(".js-poll-link").remove();
selectedTweet.find(".td-screenshot-remove").remove(); selectedTweet.find(".td-screenshot-remove").remove();
@@ -639,9 +727,13 @@
width: tweetWidth+"px" width: tweetWidth+"px"
}).appendTo(document.body); }).appendTo(document.body);
let realHeight = Math.floor(testTweet.height()); let testTweetAvatar = testTweet.find(".tweet-avatar").first();
let avatarBottom = testTweetAvatar.offset().top+testTweetAvatar.height();
let realHeight = Math.floor(Math.max(testTweet.height(), avatarBottom+10));
testTweet.remove(); testTweet.remove();
selectedTweet.find(".js-stream-item-content").first().css("height", "100vh");
$TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight); $TD.screenshotTweet(selectedTweet.html(), tweetWidth, realHeight);
} }
}; };
@@ -734,16 +826,14 @@
// 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.
// //
(function(){ (function(){
let tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?$/; const tweetRegex = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/;
const selector = "section.js-column";
let isDraggingValid = false; let isDraggingValid = false;
window.TDGF_onGlobalDragStart = function(type, data){ const events = {
isDraggingValid = (type === "link" || type === "text") && tweetRegex.test(data);
};
app.delegate("section.js-column", {
dragover: function(e){ dragover: function(e){
e.originalEvent.dataTransfer.dropEffect = isDraggingValid ? "move" : "none"; e.originalEvent.dataTransfer.dropEffect = isDraggingValid ? "all" : "none";
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}, },
@@ -767,7 +857,17 @@
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
}); };
window.TDGF_onGlobalDragStart = function(type, data){
if (type === "link"){
isDraggingValid = tweetRegex.test(data);
app.delegate(selector, events);
}
else{
app.undelegate(selector, events);
}
};
})(); })();
// //
@@ -837,7 +937,7 @@
$(".js-compose-text", ".js-docked-compose").focus(); $(".js-compose-text", ".js-docked-compose").focus();
}; };
$(".js-drawer[data-drawer='compose']").delegate(".js-account-list > .js-account-item", "click", onAccountClick); $(".js-account-list", ".js-docked-compose").delegate(".js-account-item", "click", onAccountClick);
return if !ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts"); return if !ensurePropertyExists(TD, "components", "AccountSelector", "prototype", "refreshPostingAccounts");
@@ -1174,13 +1274,52 @@
// //
// Block: Revert Like/Follow dialogs being closed after clicking an action. // Block: Revert Like/Follow dialogs being closed after clicking an action.
// //
if (ensurePropertyExists(TD, "decider", "get")){ (function(){
const prevFunc = TD.decider.get; const prevSetTimeout = window.setTimeout;
TD.decider.get = function(key){ const overrideState = function(){
return $TDX.keepLikeFollowDialogsOpen && key.startsWith("tweetdeck_subsequent_") ? false : prevFunc.apply(this, arguments); return if !$TDX.keepLikeFollowDialogsOpen;
window.setTimeout = function(func, timeout){
return timeout !== 500 && prevSetTimeout.apply(this, arguments);
}; };
};
const restoreState = function(context, key){
window.setTimeout = prevSetTimeout;
if ($TDX.keepLikeFollowDialogsOpen && key in context.state){
context.state[key] = false;
} }
};
$(document).on("uiShowFavoriteFromOptions", function(){
$(".js-btn-fav", ".js-modal-inner").each(function(){
let event = $._data(this, "events").click[0];
let handler = event.handler;
event.handler = function(){
overrideState();
handler.apply(this, arguments);
restoreState($._data(document, "events").dataFavoriteState[0].handler.context, "stopSubsequentLikes");
};
});
});
$(document).on("uiShowFollowFromOptions", function(){
$(".js-component", ".js-modal-inner").each(function(){
let event = $._data(this, "events").click[0];
let handler = event.handler;
let context = handler.context;
event.handler = function(){
overrideState();
handler.apply(this, arguments);
restoreState(context, "stopSubsequentFollows");
};
});
});
})();
// //
// Block: Fix DM reply input box not getting focused after opening a conversation. // Block: Fix DM reply input box not getting focused after opening a conversation.
@@ -1206,6 +1345,15 @@
}; };
} }
//
// Block: Remove column mouse wheel handler, which allows smooth scrolling inside columns, and horizontally scrolling column container when holding Shift.
//
if (ensurePropertyExists(TD, "ui", "columns", "setupColumn")){
TD.ui.columns.setupColumn = appendToFunction(TD.ui.columns.setupColumn, function(e){
$(".js-column[data-column='"+e.model.getKey()+"']").off("mousewheel onmousewheel");
});
}
// //
// Block: Detect and notify about connection issues. // Block: Detect and notify about connection issues.
// //

View File

@@ -12,6 +12,10 @@
height: 328px; height: 328px;
} }
#td-introduction-modal .mdl-inner {
padding-top: 0;
}
#td-introduction-modal .mdl-header-title { #td-introduction-modal .mdl-header-title {
cursor: default; cursor: default;
} }
@@ -78,7 +82,7 @@
<div class="mdl-inner"> <div class="mdl-inner">
<div class="mdl-content"> <div class="mdl-content">
<p>Thank you for downloading TweetDuck!</p> <p>Thank you for downloading TweetDuck!</p>
<p><a id="td-introduction-follow" href="#">Follow @TryTweetDuck</a> for latest news and updates about the app.</p> <p><a id="td-introduction-follow" href="#">Follow @TryMyAwesomeApp</a> for latest news and updates about the app.</p>
<div class="main-menu"></div> <div class="main-menu"></div>
<p><strong>Right-click anywhere</strong> or click <strong>Settings&nbsp;&nbsp;TweetDuck</strong> in the left panel to open the main menu. You can also right-click links, tweets, images and videos, and desktop notifications to access their respective context menus.</p> <p><strong>Right-click anywhere</strong> or click <strong>Settings&nbsp;&nbsp;TweetDuck</strong> in the left panel to open the main menu. You can also right-click links, tweets, images and videos, and desktop notifications to access their respective context menus.</p>
<p>Click <strong>Show Guide</strong> to see awesome features TweetDuck offers, or view the guide later by going to <strong>About TweetDuck</strong> and clicking the help button on top.</p> <p>Click <strong>Show Guide</strong> to see awesome features TweetDuck offers, or view the guide later by going to <strong>About TweetDuck</strong> and clicking the help button on top.</p>
@@ -102,7 +106,7 @@
onSuccess(tdUser); onSuccess(tdUser);
} }
else{ else{
TD.controller.clients.getPreferredClient().getUsersByIds([ "731137856052269056" ], users => onSuccess(users[0]), onError); TD.controller.clients.getPreferredClient().getUsersByIds([ "957608948189880320" ], users => onSuccess(users[0]), onError);
} }
}; };
@@ -113,7 +117,7 @@
$(document).trigger("uiShowFollowFromOptions", { userToFollow: user }); $(document).trigger("uiShowFollowFromOptions", { userToFollow: user });
$(".js-modals-container").find("header a[rel='user']").each(function(){ $(".js-modals-container").find("header a[rel='user']").each(function(){
this.outerHTML = this.innerText; this.outerHTML = "TweetDuck";
}); });
}, () => { }, () => {
alert("An error occurred when retrieving the account information."); alert("An error occurred when retrieving the account information.");

View File

@@ -3,22 +3,22 @@
<div class="js-tweet tweet"> <div class="js-tweet tweet">
<header class="tweet-header"> <header class="tweet-header">
<time class="tweet-timestamp js-timestamp pull-right txt-mute"> <time class="tweet-timestamp js-timestamp pull-right txt-mute">
<a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-size-variable--12">0s</a> <a target="_blank" rel="url" href="https://twitter.com/TryMyAwesomeApp" class="txt-size-variable--12">now</a>
</time> </time>
<a target="_blank" rel="user" href="https://twitter.com/chylexmc" class="account-link link-complex block"> <a target="_blank" rel="user" href="https://twitter.com/TryMyAwesomeApp" class="account-link link-complex block">
<div class="obj-left item-img tweet-img"> <div class="obj-left item-img tweet-img">
<img width="48" height="48" alt="chylexmc's avatar" src="https://pbs.twimg.com/profile_images/765161905312980992/AhDP9iY-_normal.jpg" class="tweet-avatar avatar pull-right"> <img width="48" height="48" alt="TryMyAwesomeApp's avatar" src="{avatar}" class="tweet-avatar avatar pull-right">
</div> </div>
<div class="nbfc"> <div class="nbfc">
<span class="account-inline txt-ellipsis"> <span class="account-inline txt-ellipsis">
<b class="fullname link-complex-target">chylex</b> <b class="fullname link-complex-target">TweetDuck</b>
<span class="username txt-mute">@chylexmc</span> <span class="username txt-mute">@TryMyAwesomeApp</span>
</span> </span>
</div> </div>
</a> </a>
</header> </header>
<div class="tweet-body"> <div class="tweet-body">
<p class="js-tweet-text tweet-text with-linebreaks">This is an example tweet, which lets you test the location and duration of popup notifications.</p> <p class="js-tweet-text tweet-text with-linebreaks">Here you can see the position and appearance of desktop notifications.<br><br>For location and size, you can pick a preset, or select <strong>Custom</strong> and then freely move or resize the window.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -35,6 +35,10 @@
install(plugin){ install(plugin){
this.installed.push(plugin); this.installed.push(plugin);
if (typeof plugin.obj.configure === "function"){
$TDP.setConfigurable(plugin.obj.$token);
}
if (!this.isDisabled(plugin)){ if (!this.isDisabled(plugin)){
plugin.obj.enabled(); plugin.obj.enabled();
this.runWhenReady(plugin); this.runWhenReady(plugin);
@@ -93,6 +97,13 @@
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable); window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
}; };
//
// Block: Setup a function to trigger plugin configuration.
//
window.TDPF_configurePlugin = function(identifier){
window.TD_PLUGINS.findObject(identifier).obj.configure();
};
// //
// Block: Setup a function to reload the page. // Block: Setup a function to reload the page.
// //

View File

@@ -38,7 +38,7 @@
/* Square-ify stuff */ /* Square-ify stuff */
/********************/ /********************/
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .tooltip-inner { .btn, .mdl, .mdl-content, .popover, .lst-modal, .tooltip-inner {
border-radius: 1px !important; border-radius: 1px !important;
} }
@@ -46,7 +46,7 @@
border-radius: 1px !important; border-radius: 1px !important;
} }
.compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder { .btn-compose, .app-search-fake, .app-search-input, .compose-text-container, .compose-reply-tweet, .compose-message-recipient-input-container, .compose-message-recipient, .compose-media-bar-holder, .media-grid-container, .js-quote-tweet-holder, .detail-view-inline-text {
border-radius: 0 !important; border-radius: 0 !important;
} }
@@ -94,23 +94,16 @@
/* Tweak notification layout and design */ /* Tweak notification layout and design */
/****************************************/ /****************************************/
.tweet-context .nbfc { .activity-header.has-source-avatar {
text-overflow: ellipsis !important; margin-bottom: 4px !important
white-space: nowrap !important;
}
.activity-header {
align-items: center !important;
margin-bottom: 4px !important;
} }
.activity-header .tweet-timestamp { .activity-header .tweet-timestamp {
line-height: unset !important; line-height: unset !important;
} }
.account-bio.padding-t--5 { .activity-header .icon-user-filled {
/* follow notification padding */ vertical-align: sub !important;
padding-top: 2px !important;
} }
html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu { html[data-td-theme='light'] .stream-item:not(:hover) .js-user-actions-menu {
@@ -125,6 +118,23 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
opacity: 0.25; opacity: 0.25;
} }
.stream-item[data-key^="favorite"] .item-img, .stream-item[data-key^="retweet"] .item-img {
position: absolute;
left: 21px;
top: 48px;
width: 0 !important;
}
.stream-item[data-key^="favorite"] .activity-header > .nbfc, .stream-item[data-key^="retweet"] .activity-header > .nbfc {
margin-left: 46px;
line-height: unset !important;
}
.stream-item[data-key^="favorite"] .activity-header > .nbfc > .avatar, .stream-item[data-key^="retweet"] .activity-header > .nbfc > .avatar {
position: absolute;
margin-left: -34px;
}
/***********************/ /***********************/
/* Tweaks for features */ /* Tweaks for features */
/***********************/ /***********************/
@@ -209,6 +219,17 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
cursor: pointer; cursor: pointer;
} }
.inline-reply .btn-square, .rpl-actions .btn-square {
/* remove effects from buttons under reply input... this keeps happening for some stupid reason */
background: transparent !important;
box-shadow: none !important;
}
.js-add-to-customtimeline-input {
/* the custom timeline input shadow is behaving super weird when focused */
box-shadow: none !important;
}
/***************************************************************/ /***************************************************************/
/* Fix glaring visual issues that twitter hasn't fixed yet smh */ /* Fix glaring visual issues that twitter hasn't fixed yet smh */
/***************************************************************/ /***************************************************************/
@@ -287,3 +308,15 @@ html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
.column-type-message.is-shifted-1 .username { .column-type-message.is-shifted-1 .username {
vertical-align: bottom; vertical-align: bottom;
} }
/******************************/
/* Hide unused Settings items */
/******************************/
#show-startup-notifications, #show-startup-notifications + span {
display: none;
}
#auto-play-gifs, #auto-play-gifs + span {
display: none;
}

View File

@@ -10,6 +10,10 @@ body {
overflow-y: auto !important; overflow-y: auto !important;
} }
.column {
background: transparent !important;
}
.scroll-styled-v::-webkit-scrollbar { .scroll-styled-v::-webkit-scrollbar {
width: 7px !important; width: 7px !important;
} }
@@ -34,13 +38,34 @@ body {
vertical-align: -10% !important; vertical-align: -10% !important;
} }
/************************************/
/* Favorite & retweet notifications */
/************************************/
.td-notification-padded .item-img {
position: absolute;
left: 21px;
top: 48px;
width: 0 !important;
}
.td-notification-padded .activity-header > .nbfc {
margin-left: 46px;
line-height: unset !important;
}
.td-notification-padded .activity-header > .nbfc > .avatar {
position: absolute;
margin-left: -34px;
}
/*********/ /*********/
/* Media */ /* Media */
/*********/ /*********/
.media-size-medium { .media-size-medium {
max-height: 240px; max-height: 240px;
height: calc(100vh - 16px) !important; height: calc(100vh - 20px) !important;
border-radius: 1px !important; border-radius: 1px !important;
} }
@@ -49,6 +74,11 @@ body {
} }
#tduck .js-media, #tduck .js-media-preview-container { #tduck .js-media, #tduck .js-media-preview-container {
padding-top: 1px;
margin-bottom: 2px !important;
}
#tduck .js-quote-detail .js-media, #tduck .js-quote-detail .js-media-preview-container {
margin-bottom: 0 !important; margin-bottom: 0 !important;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

BIN
Resources/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
Resources/spinner.apng Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.props')" /> <Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" />
<Import Project="packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.props')" /> <Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" />
<Import Project="packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props" Condition="Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" />
<Import Project="packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props" Condition="Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -105,6 +107,7 @@
<Compile Include="Core\Handling\RequestHandlerBase.cs" /> <Compile Include="Core\Handling\RequestHandlerBase.cs" />
<Compile Include="Core\Handling\RequestHandlerBrowser.cs" /> <Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
<Compile Include="Core\Handling\ResourceHandlerNotification.cs" /> <Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
<Compile Include="Core\ITweetDeckBrowser.cs" />
<Compile Include="Core\Notification\Example\FormNotificationExample.cs"> <Compile Include="Core\Notification\Example\FormNotificationExample.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -159,7 +162,7 @@
<Compile Include="Core\Other\FormPlugins.Designer.cs"> <Compile Include="Core\Other\FormPlugins.Designer.cs">
<DependentUpon>FormPlugins.cs</DependentUpon> <DependentUpon>FormPlugins.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\Management\VideoPlayer.cs" /> <Compile Include="Core\Management\VideoPlayer.cs" />
<Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.cs"> <Compile Include="Core\Other\Settings\Dialogs\DialogSettingsAnalytics.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -213,8 +216,7 @@
<Compile Include="Core\Utils\StringUtils.cs" /> <Compile Include="Core\Utils\StringUtils.cs" />
<Compile Include="Core\Utils\TwitterUtils.cs" /> <Compile Include="Core\Utils\TwitterUtils.cs" />
<Compile Include="Data\CombinedFileStream.cs" /> <Compile Include="Data\CombinedFileStream.cs" />
<Compile Include="Core\Other\Settings\Export\ExportFileFlags.cs" /> <Compile Include="Core\Management\ProfileManager.cs" />
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs"> <Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
<SubType>UserControl</SubType> <SubType>UserControl</SubType>
</Compile> </Compile>
@@ -248,6 +250,7 @@
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" /> <Compile Include="Core\Notification\Screenshot\TweetScreenshotManager.cs" />
<Compile Include="Data\ResourceLink.cs" />
<Compile Include="Data\Serialization\FileSerializer.cs" /> <Compile Include="Data\Serialization\FileSerializer.cs" />
<Compile Include="Data\InjectedHTML.cs" /> <Compile Include="Data\InjectedHTML.cs" />
<Compile Include="Data\Serialization\ITypeConverter.cs" /> <Compile Include="Data\Serialization\ITypeConverter.cs" />
@@ -281,6 +284,11 @@
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" /> <Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
<Compile Include="Plugins\PluginManager.cs" /> <Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Plugins\PluginScriptGenerator.cs" /> <Compile Include="Plugins\PluginScriptGenerator.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Reporter.cs" /> <Compile Include="Reporter.cs" />
<Compile Include="Updates\FormUpdateDownload.cs"> <Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
@@ -294,7 +302,7 @@
<Compile Include="Core\Other\TrayIcon.Designer.cs"> <Compile Include="Core\Other\TrayIcon.Designer.cs">
<DependentUpon>TrayIcon.cs</DependentUpon> <DependentUpon>TrayIcon.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Utils\BrowserCache.cs" /> <Compile Include="Core\Management\BrowserCache.cs" />
<Compile Include="Core\Utils\BrowserUtils.cs" /> <Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\NativeMethods.cs" /> <Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\UpdateDownloadStatus.cs" /> <Compile Include="Updates\UpdateDownloadStatus.cs" />
@@ -302,11 +310,6 @@
<Compile Include="Updates\UpdateInfo.cs" /> <Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Resources\ScriptLoader.cs" /> <Compile Include="Resources\ScriptLoader.cs" />
<Compile Include="Updates\UpdateEventArgs.cs" /> <Compile Include="Updates\UpdateEventArgs.cs" />
<Compile Include="Updates\UpdaterSettings.cs" /> <Compile Include="Updates\UpdaterSettings.cs" />
@@ -338,14 +341,10 @@
<DependentUpon>FormBrowser.cs</DependentUpon> <DependentUpon>FormBrowser.cs</DependentUpon>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Core\Other\FormAbout.resx">
<DependentUpon>FormAbout.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType> <SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -359,11 +358,13 @@
<None Include="Resources\icon-tray-new.ico" /> <None Include="Resources\icon-tray-new.ico" />
<None Include="Resources\icon-tray.ico" /> <None Include="Resources\icon-tray.ico" />
<None Include="Resources\PostBuild.ps1" /> <None Include="Resources\PostBuild.ps1" />
<None Include="Resources\spinner.apng" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Resources\Plugins\" /> <Folder Include="Resources\Plugins\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Resources\avatar.png" />
<Content Include="Resources\Scripts\code.js" /> <Content Include="Resources\Scripts\code.js" />
<Content Include="Resources\Scripts\introduction.js" /> <Content Include="Resources\Scripts\introduction.js" />
<Content Include="Resources\Scripts\notification.js" /> <Content Include="Resources\Scripts\notification.js" />
@@ -378,10 +379,6 @@
<Content Include="Resources\Scripts\update.js" /> <Content Include="Resources\Scripts\update.js" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="lib\TweetLib.Audio\TweetLib.Audio.csproj">
<Project>{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}</Project>
<Name>TweetLib.Audio</Name>
</ProjectReference>
<ProjectReference Include="subprocess\TweetDuck.Browser.csproj"> <ProjectReference Include="subprocess\TweetDuck.Browser.csproj">
<Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project> <Project>{b10b0017-819e-4f71-870f-8256b36a26aa}</Project>
<Name>TweetDuck.Browser</Name> <Name>TweetDuck.Browser</Name>
@@ -441,19 +438,17 @@ powershell -ExecutionPolicy Unrestricted -File "$(ProjectDir)Resources\PostBuild
<PropertyGroup> <PropertyGroup>
<PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent> <PreBuildEvent>powershell Get-Process TweetDuck.Browser -ErrorAction SilentlyContinue ^| Where-Object {$_.Path -eq '$(TargetDir)TweetDuck.Browser.exe'} ^| Stop-Process; Exit 0</PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="packages\cef.redist.x64.3.3239.1716\build\cef.redist.x64.targets" Condition="Exists('packages\cef.redist.x64.3.3239.1716\build\cef.redist.x64.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup> </PropertyGroup>
<Error Condition="!Exists('packages\cef.redist.x64.3.3239.1716\build\cef.redist.x64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3239.1716\build\cef.redist.x64.targets'))" /> <Error Condition="!Exists('packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x64.3.3282.1731\build\cef.redist.x64.props'))" />
<Error Condition="!Exists('packages\cef.redist.x86.3.3239.1716\build\cef.redist.x86.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3239.1716\build\cef.redist.x86.targets'))" /> <Error Condition="!Exists('packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\cef.redist.x86.3.3282.1731\build\cef.redist.x86.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.props'))" /> <Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.props'))" />
<Error Condition="!Exists('packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.targets'))" /> <Error Condition="!Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.props'))" /> <Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.props'))" />
<Error Condition="!Exists('packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.targets'))" /> <Error Condition="!Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets'))" />
</Target> </Target>
<Import Project="packages\cef.redist.x86.3.3239.1716\build\cef.redist.x86.targets" Condition="Exists('packages\cef.redist.x86.3.3239.1716\build\cef.redist.x86.targets')" /> <Import Project="packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.64.0.0-CI2497\build\CefSharp.Common.targets')" />
<Import Project="packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.63.0.0-pre01\build\CefSharp.Common.targets')" /> <Import Project="packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.64.0.0-CI2497\build\CefSharp.WinForms.targets')" />
<Import Project="packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.63.0.0-pre01\build\CefSharp.WinForms.targets')" />
</Project> </Project>

View File

@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26430.16 VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck", "TweetDuck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
EndProject EndProject
@@ -8,8 +8,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Browser", "subpro
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Audio", "lib\TweetLib.Audio\TweetLib.Audio.csproj", "{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDuck.Video", "video\TweetDuck.Video.csproj", "{278B2D11-402D-44B6-B6A1-8FA67DB65565}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetLib.Communication", "lib\TweetLib.Communication\TweetLib.Communication.csproj", "{72473763-4B9D-4FB6-A923-9364B2680F06}"
@@ -22,7 +20,6 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.ActiveCfg = Debug|x86 {2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.ActiveCfg = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Build.0 = Debug|x86 {2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Build.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Deploy.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86 {2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86 {2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86
{B10B0017-819E-4F71-870F-8256B36A26AA}.Debug|x86.ActiveCfg = Debug|x86 {B10B0017-819E-4F71-870F-8256B36A26AA}.Debug|x86.ActiveCfg = Debug|x86
@@ -32,10 +29,6 @@ Global
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86 {A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Debug|x86.ActiveCfg = Debug|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Debug|x86.Build.0 = Debug|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Release|x86.ActiveCfg = Release|x86
{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}.Release|x86.Build.0 = Release|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.ActiveCfg = Debug|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Debug|x86.Build.0 = Debug|x86
{278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86 {278B2D11-402D-44B6-B6A1-8FA67DB65565}.Release|x86.ActiveCfg = Release|x86
@@ -48,4 +41,7 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {43936FDB-CBF7-493D-A99A-24B516646584}
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -1,14 +1,14 @@
using CefSharp; using CefSharp;
using CefSharp.WinForms;
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Updates{ namespace TweetDuck.Updates{
sealed class UpdateHandler{ sealed class UpdateHandler{
private readonly ChromiumWebBrowser browser; private readonly ITweetDeckBrowser browser;
private readonly UpdaterSettings settings; private readonly UpdaterSettings settings;
public event EventHandler<UpdateEventArgs> UpdateAccepted; public event EventHandler<UpdateEventArgs> UpdateAccepted;
@@ -18,20 +18,18 @@ namespace TweetDuck.Updates{
private int lastEventId; private int lastEventId;
private UpdateInfo lastUpdateInfo; private UpdateInfo lastUpdateInfo;
public UpdateHandler(ChromiumWebBrowser browser, UpdaterSettings settings){ public UpdateHandler(ITweetDeckBrowser browser, UpdaterSettings settings){
this.browser = browser; this.browser = browser;
this.settings = settings; this.settings = settings;
browser.FrameLoadEnd += browser_FrameLoadEnd; browser.OnFrameLoaded(OnFrameLoaded);
browser.RegisterAsyncJsObject("$TDU", new Bridge(this)); browser.RegisterBridge("$TDU", new Bridge(this));
} }
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){ private void OnFrameLoaded(IFrame frame){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){ ScriptLoader.ExecuteFile(frame, "update.js");
ScriptLoader.ExecuteFile(e.Frame, "update.js");
Check(false); Check(false);
} }
}
public int Check(bool force){ public int Check(bool force){
if (Program.UserConfig.EnableUpdateCheck || force){ if (Program.UserConfig.EnableUpdateCheck || force){
@@ -39,7 +37,7 @@ namespace TweetDuck.Updates{
settings.DismissedUpdate = null; settings.DismissedUpdate = null;
} }
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases); browser.ExecuteFunction("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, settings.DismissedUpdate ?? string.Empty, settings.AllowPreReleases);
return lastEventId; return lastEventId;
} }

Binary file not shown.

View File

@@ -59,6 +59,8 @@ Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache" Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
[InstallDelete] [InstallDelete]
Type: files; Name: "{app}\TweetLib.Audio.dll"
Type: filesandordirs; Name: "{app}\scripts"
Type: filesandordirs; Name: "{app}\plugins\official" Type: filesandordirs; Name: "{app}\plugins\official"
Type: files; Name: "{app}\locales\am.pak" Type: files; Name: "{app}\locales\am.pak"
Type: files; Name: "{app}\locales\ar.pak" Type: files; Name: "{app}\locales\ar.pak"

View File

@@ -1,41 +0,0 @@
using System;
using System.Runtime.InteropServices;
using TweetLib.Audio.Impl;
namespace TweetLib.Audio{
public abstract class AudioPlayer : IDisposable{
private static bool? IsWMPAvailable;
public static AudioPlayer New(){
if (IsWMPAvailable.HasValue){
if (IsWMPAvailable.Value){
return new SoundPlayerImplWMP();
}
else{
return new SoundPlayerImplFallback();
}
}
try{
SoundPlayerImplWMP implWMP = new SoundPlayerImplWMP();
IsWMPAvailable = true;
return implWMP;
}catch(COMException){
IsWMPAvailable = false;
return new SoundPlayerImplFallback();
}
}
public abstract string SupportedFormats { get; }
public abstract event EventHandler<PlaybackErrorEventArgs> PlaybackError;
public abstract void Play(string file);
public abstract bool SetVolume(int volume);
protected abstract void Dispose(bool disposing);
public void Dispose(){
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,53 +0,0 @@
using System;
using System.IO;
using System.Media;
namespace TweetLib.Audio.Impl{
sealed class SoundPlayerImplFallback : AudioPlayer{
public override string SupportedFormats => "*.wav";
public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly SoundPlayer player;
private bool ignorePlaybackError;
public SoundPlayerImplFallback(){
player = new SoundPlayer{
LoadTimeout = 5000
};
}
public override void Play(string file){
if (player.SoundLocation != file){
player.SoundLocation = file;
ignorePlaybackError = false;
}
try{
player.Play();
}catch(FileNotFoundException e){
OnNotificationSoundError("File not found: "+e.FileName);
}catch(InvalidOperationException){
OnNotificationSoundError("File format was not recognized.");
}catch(TimeoutException){
OnNotificationSoundError("File took too long to load.");
}
}
public override bool SetVolume(int volume){
return false;
}
protected override void Dispose(bool disposing){
player.Dispose();
}
private void OnNotificationSoundError(string message){
if (!ignorePlaybackError){
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
PlaybackError?.Invoke(this, args);
ignorePlaybackError = args.Ignore;
}
}
}
}

View File

@@ -1,124 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using WMPLib;
namespace TweetLib.Audio.Impl{
sealed class SoundPlayerImplWMP : AudioPlayer{
public override string SupportedFormats => "*.wav;*.mp3;*.mp2;*.m4a;*.mid;*.midi;*.rmi;*.wma;*.aif;*.aifc;*.aiff;*.snd;*.au";
public override event EventHandler<PlaybackErrorEventArgs> PlaybackError;
private readonly Form owner;
private readonly ControlWMP wmp;
private bool wasTryingToPlay;
private bool ignorePlaybackError;
private WindowsMediaPlayer Player => wmp.Ocx;
public SoundPlayerImplWMP(){
owner = new Form();
wmp = new ControlWMP();
wmp.BeginInit();
owner.Controls.Add(wmp);
wmp.EndInit();
Player.uiMode = "none";
Player.settings.autoStart = false;
Player.settings.enableErrorDialogs = false;
Player.settings.invokeURLs = false;
Player.settings.volume = 0;
Player.MediaChange += player_MediaChange;
Player.MediaError += player_MediaError;
}
public override void Play(string file){
wasTryingToPlay = true;
try{
if (Player.URL != file){
Player.close();
Player.URL = file;
ignorePlaybackError = false;
}
else{
Player.controls.stop();
}
Player.controls.play();
}catch(Exception e){
OnNotificationSoundError("An error occurred in Windows Media Player: "+e.Message);
}
}
public override bool SetVolume(int volume){
Player.settings.volume = volume;
return true;
}
protected override void Dispose(bool disposing){
wmp.Dispose();
owner.Dispose();
}
private void player_MediaChange(object item){
IWMPMedia2 media = item as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
// ReSharper disable once CompareOfFloatsByEqualityOperator
else if (media.Error == null && media.duration == 0.0){
OnNotificationSoundError("File does not contain an audio track.");
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void player_MediaError(object pMediaObject){
IWMPMedia2 media = pMediaObject as IWMPMedia2;
if (media == null){
OnNotificationSoundError("Unknown error.");
return;
}
else if (media.Error != null){
OnNotificationSoundError(media.Error);
}
Marshal.ReleaseComObject(media);
}
private void OnNotificationSoundError(IWMPErrorItem error){
OnNotificationSoundError(error.errorCode == -1072885353 ? "Invalid media file." : error.errorDescription);
Marshal.ReleaseComObject(error);
}
private void OnNotificationSoundError(string message){
if (wasTryingToPlay){
wasTryingToPlay = false;
if (!ignorePlaybackError){
PlaybackErrorEventArgs args = new PlaybackErrorEventArgs(message);
PlaybackError?.Invoke(this, args);
ignorePlaybackError = args.Ignore;
}
}
}
[Clsid("{6bf52a52-394a-11d3-b153-00c04f79faa6}")]
private sealed class ControlWMP : AxHost{
public WindowsMediaPlayer Ocx { get; private set; }
public ControlWMP() : base("6bf52a52-394a-11d3-b153-00c04f79faa6"){}
protected override void AttachInterfaces(){
Ocx = (WindowsMediaPlayer)GetOcx();
}
}
}
}

View File

@@ -1,13 +0,0 @@
using System;
namespace TweetLib.Audio{
public sealed class PlaybackErrorEventArgs : EventArgs{
public string Message { get; }
public bool Ignore { get; set; }
public PlaybackErrorEventArgs(string message){
this.Message = message;
this.Ignore = false;
}
}
}

View File

@@ -1,35 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TweetDuck Audio Library")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TweetDuck Audio Library")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e9e1fd1b-f480-45b7-9970-be2ecfd309ac")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.1.0")]
[assembly: AssemblyFileVersion("1.1.1.0")]

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{E9E1FD1B-F480-45B7-9970-BE2ECFD309AC}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TweetLib.Audio</RootNamespace>
<AssemblyName>TweetLib.Audio</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ResolveComReferenceSilent>True</ResolveComReferenceSilent>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Debug\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="AudioPlayer.cs" />
<Compile Include="PlaybackErrorEventArgs.cs" />
<Compile Include="Impl\SoundPlayerImplFallback.cs" />
<Compile Include="Impl\SoundPlayerImplWMP.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<COMReference Include="WMPLib">
<Guid>{6BF52A50-394A-11D3-B153-00C04F79FAA6}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="cef.redist.x64" version="3.3239.1716" targetFramework="net452" xmlns="" /> <package id="cef.redist.x64" version="3.3282.1731" targetFramework="net452" xmlns="" />
<package id="cef.redist.x86" version="3.3239.1716" targetFramework="net452" xmlns="" /> <package id="cef.redist.x86" version="3.3282.1731" targetFramework="net452" xmlns="" />
<package id="CefSharp.Common" version="63.0.0-pre01" targetFramework="net452" xmlns="" /> <package id="CefSharp.Common" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
<package id="CefSharp.WinForms" version="63.0.0-pre01" targetFramework="net452" xmlns="" /> <package id="CefSharp.WinForms" version="64.0.0-CI2497" targetFramework="net452" xmlns="" />
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" /> <package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net452" xmlns="" />
</packages> </packages>

View File

@@ -3,7 +3,7 @@ using CefSharp.BrowserSubprocess;
namespace TweetDuck.Browser{ namespace TweetDuck.Browser{
static class Program{ static class Program{
internal const string Version = "1.4.0.0"; internal const string Version = "1.4.1.0";
private static int Main(string[] args){ private static int Main(string[] args){
SubProcess.EnableHighDPISupport(); SubProcess.EnableHighDPISupport();

View File

@@ -26,7 +26,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="CefSharp.BrowserSubprocess.Core, Version=63.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86"> <Reference Include="CefSharp.BrowserSubprocess.Core, Version=63.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\CefSharp.Common.63.0.0-pre01\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath> <HintPath>..\packages\CefSharp.Common.64.0.0-CI2497\CefSharp\x86\CefSharp.BrowserSubprocess.Core.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>

View File

@@ -13,7 +13,9 @@ namespace UnitTests.Data{
private class SerializationTestBasic{ private class SerializationTestBasic{
public bool TestBool { get; set; } public bool TestBool { get; set; }
public int TestInt { get; set; } public int TestInt { get; set; }
public string TestString { get; set; } public string TestStringBasic { get; set; }
public string TestStringNewLine { get; set; }
public string TestStringBackslash { get; set; }
public string TestStringNull { get; set; } public string TestStringNull { get; set; }
public TestEnum TestEnum { get; set; } public TestEnum TestEnum { get; set; }
} }
@@ -25,7 +27,9 @@ namespace UnitTests.Data{
SerializationTestBasic write = new SerializationTestBasic{ SerializationTestBasic write = new SerializationTestBasic{
TestBool = true, TestBool = true,
TestInt = -100, TestInt = -100,
TestString = "abc"+Environment.NewLine+"def", TestStringBasic = "hello123",
TestStringNewLine = "abc"+Environment.NewLine+"def"+Environment.NewLine,
TestStringBackslash = @"C:\Test\\\Abc\",
TestStringNull = null, TestStringNull = null,
TestEnum = TestEnum.D TestEnum = TestEnum.D
}; };
@@ -38,7 +42,9 @@ namespace UnitTests.Data{
Assert.IsTrue(read.TestBool); Assert.IsTrue(read.TestBool);
Assert.AreEqual(-100, read.TestInt); Assert.AreEqual(-100, read.TestInt);
Assert.AreEqual("abc"+Environment.NewLine+"def", read.TestString); Assert.AreEqual("hello123", read.TestStringBasic);
Assert.AreEqual("abc"+Environment.NewLine+"def"+Environment.NewLine, read.TestStringNewLine);
Assert.AreEqual(@"C:\Test\\\Abc\", read.TestStringBackslash);
Assert.IsNull(read.TestStringNull); Assert.IsNull(read.TestStringNull);
Assert.AreEqual(TestEnum.D, read.TestEnum); Assert.AreEqual(TestEnum.D, read.TestEnum);
} }

View File

@@ -75,6 +75,7 @@ namespace TweetDuck.Video{
} }
private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){ private void pipe_DataIn(object sender, DuplexPipe.PipeReadEventArgs e){
Invoke(new Action(() => {
switch(e.Key){ switch(e.Key){
case "key": case "key":
HandleKey((Keys)int.Parse(e.Data, NumberStyles.Integer)); HandleKey((Keys)int.Parse(e.Data, NumberStyles.Integer));
@@ -84,6 +85,7 @@ namespace TweetDuck.Video{
StopVideo(); StopVideo();
break; break;
} }
}));
} }
private void player_PlayStateChange(int newState){ private void player_PlayStateChange(int newState){
@@ -102,8 +104,10 @@ namespace TweetDuck.Video{
} }
private void player_MediaError(object pMediaObject){ private void player_MediaError(object pMediaObject){
Console.Out.WriteLine(((IWMPMedia2)pMediaObject).Error.errorDescription); IWMPErrorItem error = ((IWMPMedia2)pMediaObject).Error;
Console.Out.WriteLine($"Media Error {error.errorCode}: {error.errorDescription}");
Marshal.ReleaseComObject(error);
Marshal.ReleaseComObject(pMediaObject); Marshal.ReleaseComObject(pMediaObject);
Environment.Exit(Program.CODE_MEDIA_ERROR); Environment.Exit(Program.CODE_MEDIA_ERROR);
} }

View File

@@ -5,7 +5,7 @@ using System.Windows.Forms;
namespace TweetDuck.Video{ namespace TweetDuck.Video{
static class Program{ static class Program{
internal const string Version = "1.2.1.0"; internal const string Version = "1.2.2.1";
// referenced in VideoPlayer // referenced in VideoPlayer
// set by task manager -- public const int CODE_PROCESS_KILLED = 1; // set by task manager -- public const int CODE_PROCESS_KILLED = 1;
@@ -40,7 +40,7 @@ namespace TweetDuck.Video{
try{ try{
Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken)); Application.Run(new FormPlayer(ownerHandle, defaultVolume, videoUrl, pipeToken));
}catch(Exception e){ }catch(Exception e){
Console.Out.WriteLine(e.Message); Console.Out.WriteLine(e);
return CODE_LAUNCH_FAIL; return CODE_LAUNCH_FAIL;
} }