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

Compare commits

...

130 Commits
1.10 ... 1.12

Author SHA1 Message Date
a2a5dfd435 Reset the official plugins folder in the update installer 2018-01-01 01:27:58 +01:00
f3d7c8d4c3 Set language header to match app locale 2017-12-31 14:21:21 +01:00
67f60dd787 Merge pull request #190 from chylex/locale_magic
Add options for app locale (remove argument) & target translation language
2017-12-31 14:03:15 +01:00
62310ce4a4 Add an option to set target language for tweet translations 2017-12-31 13:59:19 +01:00
3a27089364 Add an option to set app locale in a new Options tab & remove locale argument 2017-12-31 13:29:38 +01:00
a05460f562 Make BrowserCache.CacheFolder a property 2017-12-31 12:14:45 +01:00
390872c305 Merge pull request #189 from chylex/settings_ui_plz 2017-12-31 10:26:23 +01:00
594d12df79 Reorganize all Options tabs into FlowLayoutPanels 2017-12-31 10:20:42 +01:00
c42c12c72b Move Options tooltips outside designer files & reorder options in code 2017-12-31 08:10:24 +01:00
c37f4fe365 Merge branch 'master' of https://github.com/chylex/TweetDuck 2017-12-31 07:02:20 +01:00
8d37d68770 Fix dragging links not stripping t.co shortener
Closes #183
2017-12-31 06:59:17 +01:00
1b3d1fb36a Tweak readme wording 2017-12-31 05:36:32 +01:00
2f352ef9bb Update CefSharp to 63-pre01 and update documentation 2017-12-31 05:34:36 +01:00
527f3cab4c Fix reinstantiating AnalyticsManager when restoring from tray & test stuff 2017-12-31 05:01:23 +01:00
f67bf27db2 Add an option to automatically clear cache after exceeding a set size
Closes #182
2017-12-30 02:06:36 +01:00
a8bb3ba349 Improve performance and safety of cache size checking & clearing 2017-12-28 04:52:53 +01:00
321ab12213 WTF is AnyCPU doing in the project files 2017-12-28 00:50:51 +01:00
4cab18e557 Merge pull request #187 from chylex/cefsharp63
Update CefSharp to 63 (early build) & delete GC reload
2017-12-28 00:39:07 +01:00
c15ea97a36 Update CEF again 2017-12-28 00:32:26 +01:00
a0cc4109df Delete GC reload in CEF 63 (#186) 2017-12-28 00:03:24 +01:00
f66ff1000a Replace AbstractRequestHandler with CefSharp's implementation 2017-12-21 22:45:29 +01:00
51a9bb6d3c Update CefSharp to 63 pre-release 2017-12-21 20:25:36 +01:00
07017bd29b Fix cut off usernames in Messages column 2017-12-21 20:15:02 +01:00
45b6f49a08 Fix middle-click and ctrl-click handling in CEF 62 2017-12-07 20:07:15 +01:00
103ad72788 Update CefSharp to 62 (early build) 2017-12-07 19:50:43 +01:00
543259f29f Release 1.11.2 2017-11-21 07:07:24 +01:00
98799734c5 Add a box shadow to main menu in the guide 2017-11-21 06:59:54 +01:00
96f491a666 Redo the introduction dialog (add main menu image & follow link, rewrite text) 2017-11-21 06:11:49 +01:00
29e541dbef Fix comment formatting in notification.css 2017-11-20 18:01:17 +01:00
1343b9c113 Fix username alignment (follow notifications & all types in desktop notifications) 2017-11-20 18:01:02 +01:00
94920fd459 Ensure only one guide window is open and fix webkit element outline 2017-11-20 17:44:28 +01:00
b2f3b245b7 Open TweetDuck guide links directly in the app 2017-11-20 17:25:25 +01:00
15bc6c1d73 Fix idiot chromium being unable to figure out window size while loading guide 2017-11-20 17:21:46 +01:00
2c175b8d3a Fix incorrect default config value for notification scroll speed 2017-11-18 15:31:36 +01:00
a48c17a769 Update analytics (fix system edition, add dev tools and other feature collection) 2017-11-18 07:15:18 +01:00
03465c4ab0 Remove dismissed update config entry after accepting an update 2017-11-18 02:53:25 +01:00
b4e936c530 Minor refactoring of notification classes & remove no longer needed CSS 2017-11-14 19:06:05 +01:00
fb1482370a Fix issues from TweetDeck updates (long usernames, badge in detail view, notification media previews) 2017-11-14 18:42:34 +01:00
e831bc2bea Fix broken compose text size in edit-design plugin after a TweetDeck update 2017-11-11 17:32:20 +01:00
c74c168c96 Release 1.11.1 2017-11-10 14:59:48 +01:00
40b53fa40c Fix broken update notification CSS after a TweetDeck update 2017-11-10 14:52:32 +01:00
3481cc0349 Fix broken CSS in browser and plugins after a TweetDeck update 2017-11-10 14:44:47 +01:00
09abd889e9 Add ID to <html> for selector priority 2017-11-10 14:14:50 +01:00
330bbfbb31 Fix broken CSS in notifications after a TweetDeck update 2017-11-10 13:35:29 +01:00
6b7b690476 Remove Log method from the bridge object 2017-11-09 21:43:22 +01:00
cea72801a7 Fix video player process sometimes taking too long to close 2017-11-09 19:29:01 +01:00
04369e22a7 Add option to disable animated avatars (general GIF toggle)
Note: this breaks when disable-extensions is used, so it was changed to
disable-pdf-extension instead
2017-11-09 18:18:22 +01:00
f1b16eab9a Add a global function (including one for plugins) to reload columns 2017-11-09 18:15:58 +01:00
13646d9c90 Increase quality of media previews in desktop notifications 2017-11-09 17:08:39 +01:00
17d762ce91 Make spell check option not require a restart 2017-11-09 16:51:40 +01:00
edb40adaa1 Release 1.11 2017-11-09 14:40:09 +01:00
bc0809994a Fix import & restart overwriting some imported settings such as zoom, add a TODO for system config import/export 2017-11-08 12:23:58 +01:00
a3e3d517b0 Fix high DPI for dialogs displayed before the main window 2017-11-08 11:39:32 +01:00
d8b63a54ca Update the guide 2017-11-08 10:56:05 +01:00
b81e7583eb Implement feature usage analytics 2017-11-08 08:15:45 +01:00
51f9ba3642 Add option to always show character count to edit-design plugin 2017-11-08 06:40:36 +01:00
296626f7c7 Implement a notification about previously enabled anonymous data sending 2017-11-07 21:37:41 +01:00
5b2daf9746 Refactor method order and return types in config file classes 2017-11-07 18:36:52 +01:00
9a6b615174 Cleanup FileSerializer Write/Read calls & change exception for empty files 2017-11-07 18:10:38 +01:00
18f8d5b269 Tweak key format in analytics request 2017-11-04 14:07:24 +01:00
2867a875c9 "Fix" an uncommon video player crash when closing short videos
Closes #177
2017-11-04 14:03:28 +01:00
ee2f5ae8cb Disable default TweetDeck update notification 2017-11-04 03:30:50 +01:00
bd5c301fb9 Refactor old icon style declaration in edit-design and add !important 2017-11-02 16:45:26 +01:00
6df68629f7 Implement system config export/import/reset (without UI) 2017-11-02 13:34:51 +01:00
be08fd4445 Remove UserConfig.ZoomMultiplier and old migration code 2017-11-02 13:20:19 +01:00
6f1afb94fb Add a global function to reprioritize events and refactor stuff 2017-11-02 11:02:00 +01:00
7401b8a52d Fix search input not being unfocused after opening search externally 2017-11-02 10:10:04 +01:00
c83b62ebaa Make searching while holding Ctrl or middle-clicking search icon open search externally 2017-11-02 09:46:33 +01:00
108cf8923e Implement analytics (#176)
* Implement analytics report generation w/ user config and basic system info

* Add HW and plugin info to analytics report generation

* Add a way of displaying the full analytics report

* Fix issues in analytics report and include design theme

* Ensure tab config is saved when switching tabs

* Fix compilation error in TabSettingsFeedback and safeguard nulls

* Add locale to analytics report

* Work on analytics (utils, last collection label, dependency refactoring)

* Add analytics state file and implement sending reports every week

* Send an analytics report after each update
2017-11-02 03:08:43 +01:00
4e26fd9d56 Random refactoring 2017-11-01 04:02:44 +01:00
8c9168a4bf Temporarily move notification with Alt and restore by middle-clicking title bar
Closes #175
2017-10-31 23:40:56 +01:00
97da0b79e4 Fix compile error after removing TimerBarHeight constant 2017-10-31 23:34:00 +01:00
d7e5f6876b Increase notification timer height with higher DPI 2017-10-31 23:10:08 +01:00
1b92b112e2 Refactor FormNotificationBase.GetBorderStyle 2017-10-31 12:50:34 +01:00
ca55119531 Rewrite CanMoveWindow and CanResizeWindow in notification classes 2017-10-31 12:36:46 +01:00
d9da14b5dc Make the example notification its own class 2017-10-31 12:01:53 +01:00
512b5666ac Fix 'Escape' key not clearing notification tweet queue & refactor HideNotification 2017-10-31 11:31:01 +01:00
64977964e8 Bump project versions 2017-10-31 10:53:42 +01:00
2bc13e0de6 Remove subprocess dependency on communication lib & remove Comms class 2017-10-31 10:51:33 +01:00
b90c5f17cf Fix misaligned Plugins form controls when reloading plugins after scrolling down 2017-10-31 07:18:38 +01:00
7d8d0bd43b Refactor awful plugin loading and management code 2017-10-31 07:13:17 +01:00
Alexander
54c1137927 Fix warning about possible null reference in audio playback error event (#174)
* Fix V3083 warnings from PVS-Studio Static Analyzer
2017-10-29 19:32:18 +01:00
e6655219ee Add a context menu item to apply ROT13 to text selection in inputs 2017-10-29 04:39:28 +01:00
5896f8e35a Allow only px values in custom items in edit-design plugin 2017-10-25 22:12:28 +02:00
934cba7251 Fix link expansion & tooltips not working with long domains (ellipsis in front)
Closes #172
2017-10-25 19:23:02 +02:00
9cc1a11bef Fix a rare issue where GIFs open in browser due to a missing source URL 2017-10-23 00:25:23 +02:00
c1bc956d6d Move TrayIcon to a different namespace 2017-10-21 15:46:52 +02:00
351b174b86 Refactor TweetDeck browser into a separate class 2017-10-21 15:35:46 +02:00
0b4aaf80dc Remove unnecessary usings and code 2017-10-21 14:53:50 +02:00
c10c185817 Release 1.10.3 2017-10-19 00:43:06 +02:00
327ef1cbee Restart TweetDuck if user declines UAC when updating and improve error handling 2017-10-19 00:30:37 +02:00
15eb823c7f Replace OpenExternalBrowserUnsafe with the new OpenAssociatedProgram 2017-10-19 00:24:40 +02:00
54613e5242 Handle errors when opening images in associated viewer 2017-10-19 00:21:53 +02:00
df1352cbe3 Update README.md 2017-10-18 23:50:23 +02:00
0559afd972 Make middle-click tweet actions work in detail view 2017-10-18 16:50:34 +02:00
afffca020e Add context menu item to view images in photo viewer 2017-10-18 13:50:20 +02:00
d663cc3f64 Add username to default video download filename & tweak playback error message 2017-10-17 19:39:35 +02:00
110d41e393 Add function to trigger video playback for plugins 2017-10-17 13:16:19 +02:00
1a8823f592 Fix clipboard html stripping crashing with no text data
Closes #171
2017-10-17 11:22:38 +02:00
6374a852b0 Merge branch 'master' of https://github.com/chylex/TweetDuck 2017-10-16 16:37:24 +02:00
a10c7dd7c3 Fix README heading links 2017-10-16 16:36:30 +02:00
547c7ea417 Update README heading links (still bork on GitHub) 2017-10-16 16:33:41 +02:00
760607995a Update README and post build log message 2017-10-16 16:30:55 +02:00
4704197c09 Add option for larger quote font size to edit-design & update modal layout 2017-10-16 15:38:19 +02:00
093ac1ac40 Make middle-click instant quote work with reply-account plugin 2017-10-16 09:32:52 +02:00
9ed8b0d904 Fix used account in updated middle-click actions 2017-10-15 23:48:56 +02:00
7346ce370d Add middle-click actions (reply popout, RT quote, fav modal) & fix popout in temp columns 2017-10-15 22:59:56 +02:00
adefdadc19 Fix border-radius when quoting a tweet 2017-10-15 22:29:44 +02:00
703bce2d00 Fix PostBuild.ps1 errors not causing failed build & refactor 2017-10-13 13:09:50 +02:00
97928ecd84 Check emoji-ordering.txt for carriage return on build 2017-10-13 12:54:15 +02:00
be9ea7f64a Fuck everything about gitattributes 2017-10-13 12:39:03 +02:00
ec2aaa8789 Add exact emoji name match detection to emoji keyboard 2017-10-13 12:20:54 +02:00
ab14b72526 Force LF in emoji-ordering.txt and other txt/js files 2017-10-13 12:16:39 +02:00
d8e304f3c1 Release 1.10.2 2017-10-12 13:01:47 +02:00
ea53ce361f Bump edit-design plugin version 2017-10-12 10:58:12 +02:00
2fce80b347 Fix custom font size in edit-design & time font in example notification 2017-10-11 20:42:42 +02:00
373c0b1cc3 Fix separator before 'Open dev tools' in example notification context menu 2017-10-11 20:34:50 +02:00
e5e1b7e608 Fix TweetDeck being unable to detect OS name 2017-10-11 20:33:39 +02:00
7e9221c9e0 Merge branch 'master' of https://github.com/chylex/TweetDuck 2017-10-11 20:24:09 +02:00
6b849f854e Rewrite font size & theme handling, add <html> attributes to notifications 2017-10-11 20:22:32 +02:00
831f6bc744 Add support section to readme 2017-10-10 12:25:52 +02:00
d282a7a537 Fix border-radius in media upload previews 2017-10-08 05:43:54 +02:00
fb2f1e3031 Release 1.10.1 2017-10-08 05:27:38 +02:00
00a0da3df3 Fix detail view screenshots & tweak screenshot timings for iframes separately 2017-10-08 01:42:53 +02:00
8c447b1ffb Maybe fix dragged tweet links not being recognized from certain drag sources 2017-09-29 18:04:44 +02:00
a4841175e8 Fix more border-radius, "Ready to Tweet" alignment, and docked reply close icon position 2017-09-29 16:52:39 +02:00
9b139132a1 Disable reply middle-click in temporary columns & fix random reloads from middle-clicks 2017-09-29 14:58:58 +02:00
4a404ecabc Fix weird button styles in inline replies in modal dialogs 2017-09-29 12:48:00 +02:00
aee758b559 Update reply-account docs & fix error with temporary columns and advanced selector 2017-09-29 12:47:13 +02:00
be060d0386 Add new info about emoji usage to the guide 2017-09-28 17:29:34 +02:00
120 changed files with 3518 additions and 1937 deletions

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
# Auto detect text files and perform LF normalization
* text=auto
*.txt text eof=lf
*.cs diff=csharp

View File

@@ -5,7 +5,6 @@ namespace TweetDuck.Configuration{
static class Arguments{ static class Arguments{
// public args // public args
public const string ArgDataFolder = "-datafolder"; public const string ArgDataFolder = "-datafolder";
public const string ArgLocale = "-locale";
public const string ArgLogging = "-log"; public const string ArgLogging = "-log";
public const string ArgDebugUpdates = "-debugupdates"; public const string ArgDebugUpdates = "-debugupdates";

View File

@@ -4,7 +4,6 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetLib.Communication;
namespace TweetDuck.Configuration{ namespace TweetDuck.Configuration{
sealed class LockManager{ sealed class LockManager{
@@ -136,13 +135,11 @@ namespace TweetDuck.Configuration{
// Locking process // Locking process
public bool RestoreLockingProcess(int failTimeout){ public bool RestoreLockingProcess(int failTimeout){
if (lockingProcess != null){ if (lockingProcess != null && lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
if (lockingProcess.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
Comms.BroadcastMessage(Program.WindowRestoreMessage, (uint)lockingProcess.Id, 0);
if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){ if (WindowsUtils.TrySleepUntil(() => CheckLockingProcessExited() || (lockingProcess.MainWindowHandle != IntPtr.Zero && lockingProcess.Responding), failTimeout, RetryDelay)){
return true; return true;
}
} }
} }
@@ -166,15 +163,12 @@ namespace TweetDuck.Configuration{
lockingProcess = null; lockingProcess = null;
return true; return true;
} }
}catch(Exception ex){ }catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
if (ex is InvalidOperationException || ex is Win32Exception){ if (lockingProcess != null){
if (lockingProcess != null){ bool hasExited = CheckLockingProcessExited();
bool hasExited = CheckLockingProcessExited(); lockingProcess.Dispose();
lockingProcess.Dispose(); return hasExited;
return hasExited;
}
} }
else throw;
} }
} }

View File

@@ -1,11 +1,12 @@
using System; using System;
using System.IO; using System.IO;
using TweetDuck.Core.Utils;
using TweetDuck.Data.Serialization; 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"));
@@ -14,8 +15,8 @@ namespace TweetDuck.Configuration{
private bool _hardwareAcceleration = true; private bool _hardwareAcceleration = true;
public bool EnableBrowserGCReload { get; set; } = true; public bool ClearCacheAutomatically { get; set; } = true;
public int BrowserMemoryThreshold { get; set; } = 400; public int ClearCacheThreshold { get; set; } = 250;
// SPECIAL PROPERTIES // SPECIAL PROPERTIES
@@ -32,14 +33,11 @@ namespace TweetDuck.Configuration{
this.file = file; this.file = file;
} }
public bool Save(){ public void Save(){
try{ try{
WindowsUtils.CreateDirectoryForFile(file);
Serializer.Write(file, this); Serializer.Write(file, this);
return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not save the system configuration file.", true, e);
return false;
} }
} }
@@ -47,9 +45,7 @@ namespace TweetDuck.Configuration{
SystemConfig config = new SystemConfig(file); SystemConfig config = new SystemConfig(file);
try{ try{
Serializer.Read(file, config); Serializer.ReadIfExists(file, config);
}catch(FileNotFoundException){
}catch(DirectoryNotFoundException){
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not open the system configuration file. If you continue, you will lose system specific configuration such as Hardware Acceleration.", true, e);
} }

View File

@@ -1,26 +1,16 @@
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using TweetDuck.Core;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Notification; using TweetDuck.Core.Notification;
using TweetDuck.Core.Other;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Data.Serialization; 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>{ HandleUnknownProperties = HandleUnknownProperties }; private static readonly FileSerializer<UserConfig> Serializer = new FileSerializer<UserConfig>();
private static void HandleUnknownProperties(UserConfig obj, Dictionary<string, string> data){
data.Remove("EnableBrowserGCReload");
data.Remove("BrowserMemoryThreshold");
if (data.Count == 0){
obj.Save();
}
}
static UserConfig(){ static UserConfig(){
Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter); Serializer.RegisterTypeConverter(typeof(WindowState), WindowState.Converter);
@@ -44,8 +34,9 @@ namespace TweetDuck.Configuration{
// CONFIGURATION DATA // CONFIGURATION DATA
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();
@@ -54,11 +45,15 @@ namespace TweetDuck.Configuration{
public bool SwitchAccountSelectors { get; set; } = true; public bool SwitchAccountSelectors { get; set; } = true;
public bool OpenSearchInFirstColumn { get; set; } = true; public bool OpenSearchInFirstColumn { get; set; } = true;
public bool BestImageQuality { get; set; } = true; public bool BestImageQuality { get; set; } = true;
public bool EnableSpellCheck { get; set; } = false; public bool EnableAnimatedImages { get; set; } = true;
public int VideoPlayerVolume { get; set; } = 50; public int VideoPlayerVolume { get; set; } = 50;
private int _zoomLevel = 100; private int _zoomLevel = 100;
private bool _muteNotifications; private bool _muteNotifications;
public bool EnableSpellCheck { get; set; } = false;
public string AppLocale { get; set; } = "en-US";
public string TranslationTarget { get; set; } = "en";
private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled; private TrayIcon.Behavior _trayBehavior = TrayIcon.Behavior.Disabled;
public bool EnableTrayHighlight { get; set; } = true; public bool EnableTrayHighlight { get; set; } = true;
@@ -82,7 +77,7 @@ namespace TweetDuck.Configuration{
public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto; public TweetNotification.Size NotificationSize { get; set; } = TweetNotification.Size.Auto;
public Size CustomNotificationSize { get; set; } = Size.Empty; public Size CustomNotificationSize { get; set; } = Size.Empty;
public int NotificationScrollSpeed { get; set; } = 10; public int NotificationScrollSpeed { get; set; } = 100;
public int NotificationSoundVolume { get; set; } = 100; public int NotificationSoundVolume { get; set; } = 100;
private string _notificationSoundPath; private string _notificationSoundPath;
@@ -125,8 +120,6 @@ namespace TweetDuck.Configuration{
} }
} }
public double ZoomMultiplier => _zoomLevel/100.0;
public TrayIcon.Behavior TrayBehavior{ public TrayIcon.Behavior TrayBehavior{
get => _trayBehavior; get => _trayBehavior;
@@ -152,10 +145,8 @@ namespace TweetDuck.Configuration{
this.file = file; this.file = file;
} }
public bool Save(){ public void Save(){
try{ try{
WindowsUtils.CreateDirectoryForFile(file);
if (File.Exists(file)){ if (File.Exists(file)){
string backupFile = GetBackupFile(file); string backupFile = GetBackupFile(file);
File.Delete(backupFile); File.Delete(backupFile);
@@ -163,34 +154,33 @@ namespace TweetDuck.Configuration{
} }
Serializer.Write(file, this); Serializer.Write(file, this);
return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not save the configuration file.", true, e);
return false;
} }
} }
public bool Reload(){ public void Reload(){
try{ try{
LoadInternal(false); LoadInternal(false);
return true;
}catch(FileNotFoundException){ }catch(FileNotFoundException){
try{ try{
Serializer.Write(file, new UserConfig(file)); Serializer.Write(file, new UserConfig(file));
LoadInternal(false); LoadInternal(false);
return true;
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not regenerate configuration file.", true, e);
return false;
} }
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e); Program.Reporter.HandleException("Configuration Error", "Could not reload configuration file.", true, e);
return false;
} }
} }
private void LoadInternal(bool backup){ private void LoadInternal(bool backup){
Serializer.Read(backup ? GetBackupFile(file) : file, this); Serializer.Read(backup ? GetBackupFile(file) : file, this);
if (NotificationScrollSpeed == 10){ // incorrect initial value
NotificationScrollSpeed = 100;
Save();
}
} }
public static UserConfig Load(string file){ public static UserConfig Load(string file){

View File

@@ -7,9 +7,8 @@ namespace TweetDuck.Core.Bridge{
} }
public static string GenerateScript(Environment environment){ public static string GenerateScript(Environment environment){
string Bool(bool value){ string Bool(bool value) => value ? "true;" : "false;";
return value ? "true;" : "false;"; string Str(string value) => '"'+value+"\";";
}
StringBuilder build = new StringBuilder().Append("(function(x){"); StringBuilder build = new StringBuilder().Append("(function(x){");
@@ -21,6 +20,7 @@ namespace TweetDuck.Core.Bridge{
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.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));
} }
if (environment == Environment.Notification){ if (environment == Environment.Notification){

View File

@@ -11,8 +11,8 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Bridge{ namespace TweetDuck.Core.Bridge{
sealed class TweetDeckBridge{ sealed class TweetDeckBridge{
public static string FontSizeClass; public static string FontSize { get; private set; }
public static string NotificationHeadContents; public static string NotificationHeadLayout { get; private set; }
public static string LastHighlightedTweetUrl = string.Empty; public static string LastHighlightedTweetUrl = string.Empty;
public static string LastHighlightedQuoteUrl = string.Empty; public static string LastHighlightedQuoteUrl = string.Empty;
@@ -25,7 +25,7 @@ namespace TweetDuck.Core.Bridge{
private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2); private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
public static void ResetStaticProperties(){ public static void ResetStaticProperties(){
FontSizeClass = NotificationHeadContents = null; FontSize = NotificationHeadLayout = null;
LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty; LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
} }
@@ -56,12 +56,11 @@ namespace TweetDuck.Core.Bridge{
}); });
} }
public void LoadFontSizeClass(string fsClass){ public void LoadNotificationLayout(string fontSize, string headLayout){
form.InvokeAsyncSafe(() => FontSizeClass = fsClass); form.InvokeAsyncSafe(() => {
} FontSize = fontSize;
NotificationHeadLayout = headLayout;
public void LoadNotificationHeadContents(string headContents){ });
form.InvokeAsyncSafe(() => NotificationHeadContents = headContents);
} }
public void SetLastRightClickInfo(string type, string link){ public void SetLastRightClickInfo(string type, string link){
@@ -118,8 +117,8 @@ namespace TweetDuck.Core.Bridge{
form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height)); form.InvokeAsyncSafe(() => form.OnTweetScreenshotReady(html, width, height));
} }
public void PlayVideo(string url){ public void PlayVideo(string url, string username){
form.InvokeAsyncSafe(() => form.PlayVideo(url)); form.InvokeAsyncSafe(() => form.PlayVideo(url, username));
} }
public void FixClipboard(){ public void FixClipboard(){
@@ -149,13 +148,9 @@ namespace TweetDuck.Core.Bridge{
public void CrashDebug(string message){ public void CrashDebug(string message){
#if DEBUG #if DEBUG
Log(message); System.Diagnostics.Debug.WriteLine(message);
System.Diagnostics.Debugger.Break(); System.Diagnostics.Debugger.Break();
#endif #endif
} }
public void Log(string data){
System.Diagnostics.Debug.WriteLine(data);
}
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Drawing; using System;
using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDuck.Core.Controls{ namespace TweetDuck.Core.Controls{
@@ -13,7 +14,7 @@ namespace TweetDuck.Core.Controls{
} }
public void SetValueInstant(int value){ public void SetValueInstant(int value){
ControlExtensions.SetValueInstant(this, value); ControlExtensions.SetValueInstant(this, Math.Max(Minimum, Math.Min(Maximum, value)));
} }
protected override void OnPaint(PaintEventArgs e){ protected override void OnPaint(PaintEventArgs e){

View File

@@ -24,7 +24,7 @@
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.trayIcon = new TweetDuck.Core.TrayIcon(this.components); this.trayIcon = new TweetDuck.Core.Other.TrayIcon(this.components);
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.timerResize = new System.Windows.Forms.Timer(this.components); this.timerResize = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout(); this.SuspendLayout();
@@ -57,7 +57,7 @@
#endregion #endregion
private TrayIcon trayIcon; private TweetDuck.Core.Other.TrayIcon trayIcon;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Timer timerResize; private System.Windows.Forms.Timer timerResize;
} }

View File

@@ -1,23 +1,19 @@
using CefSharp; using System;
using CefSharp.WinForms;
using System;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; 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.Handling.General;
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.Management; using TweetDuck.Core.Other.Management;
using TweetDuck.Core.Other.Settings; using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins; using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events; using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Audio; using TweetLib.Audio;
@@ -44,20 +40,19 @@ namespace TweetDuck.Core{
public string UpdateInstallerPath { get; private set; } public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser; private readonly TweetDeckBrowser browser;
private readonly PluginManager plugins; private readonly PluginManager plugins;
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private readonly FormNotificationTweet notification; private readonly FormNotificationTweet notification;
private readonly ContextMenu contextMenu; private readonly ContextMenu contextMenu;
private readonly MemoryUsageTracker memoryUsageTracker;
private bool isLoaded; private bool isLoaded;
private bool isBrowserReady;
private FormWindowState prevState; private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager; private TweetScreenshotManager notificationScreenshotManager;
private SoundNotification soundNotification; private SoundNotification soundNotification;
private VideoPlayer videoPlayer; private VideoPlayer videoPlayer;
private AnalyticsManager analytics;
public FormBrowser(UpdaterSettings updaterSettings){ public FormBrowser(UpdaterSettings updaterSettings){
InitializeComponent(); InitializeComponent();
@@ -67,52 +62,21 @@ namespace TweetDuck.Core{
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath); this.plugins = new PluginManager(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.PluginChangedState += plugins_PluginChangedState;
this.plugins.Reload(); this.plugins.Reload();
this.contextMenu = ContextMenuBrowser.CreateMenu(this); this.notification = new FormNotificationTweet(this, plugins);
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
this.notification = new FormNotificationTweet(this, plugins){
#if DEBUG
CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt
#else
CanMoveWindow = () => false
#endif
};
this.notification.Show(); this.notification.Show();
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){ this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge(this, notification));
DialogHandler = new FileDialogHandler(), this.browser.PageLoaded += browser_PageLoaded;
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(this),
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(this),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser()
};
#if DEBUG this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
this.browser.FrameLoadStart += browser_FrameLoadStart;
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
this.browser.LoadError += browser_LoadError;
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation;
Controls.Add(browser);
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) => {
memoryUsageTracker.Dispose(); Config.MuteToggled -= Config_MuteToggled;
Config.TrayBehaviorChanged -= Config_TrayBehaviorChanged;
browser.Dispose(); browser.Dispose();
contextMenu.Dispose(); contextMenu.Dispose();
@@ -120,21 +84,25 @@ namespace TweetDuck.Core{
notificationScreenshotManager?.Dispose(); notificationScreenshotManager?.Dispose();
soundNotification?.Dispose(); soundNotification?.Dispose();
videoPlayer?.Dispose(); videoPlayer?.Dispose();
analytics?.Dispose();
}; };
Config.MuteToggled += Config_MuteToggled;
this.trayIcon.ClickRestore += trayIcon_ClickRestore; this.trayIcon.ClickRestore += trayIcon_ClickRestore;
this.trayIcon.ClickClose += trayIcon_ClickClose; this.trayIcon.ClickClose += trayIcon_ClickClose;
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged; Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
UpdateTrayIcon(); UpdateTrayIcon();
Config.MuteToggled += Config_MuteToggled; this.updates = browser.CreateUpdateHandler(updaterSettings);
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
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;
if (Config.AllowDataCollection){
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
}
RestoreWindow(); RestoreWindow();
} }
@@ -156,81 +124,11 @@ namespace TweetDuck.Core{
isLoaded = true; isLoaded = true;
} }
private void OnBrowserReady(){
if (!isBrowserReady){
browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill;
isBrowserReady = true;
}
}
private void UpdateTrayIcon(){ private void UpdateTrayIcon(){
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon(); trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
} }
// active event handlers // event handlers
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word);
}
BeginInvoke(new Action(OnBrowserReady));
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
if (e.Frame.IsMain){
memoryUsageTracker.Stop();
if (Config.ZoomLevel != 100){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
}
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
}
}
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties(PropertyBridge.Environment.Browser);
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js");
InjectBrowserCSS();
ReinjectCustomCSS(Config.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
TweetDeckBridge.ResetStaticProperties();
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
}
if (Config.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
}
}
}
private void browser_LoadError(object sender, LoadErrorEventArgs e){
if (e.ErrorCode == CefErrorCode.Aborted){
return;
}
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
}
}
}
private void timerResize_Tick(object sender, EventArgs e){ private void timerResize_Tick(object sender, EventArgs e){
FormBrowser_ResizeEnd(this, e); // also stops timer FormBrowser_ResizeEnd(this, e); // also stops timer
@@ -301,11 +199,7 @@ namespace TweetDuck.Core{
} }
private void Config_MuteToggled(object sender, EventArgs e){ private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Environment.Browser); TriggerAnalyticsEvent(AnalyticsFile.Event.MuteNotification);
}
private void Config_ZoomLevelChanged(object sender, EventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
} }
private void Config_TrayBehaviorChanged(object sender, EventArgs e){ private void Config_TrayBehaviorChanged(object sender, EventArgs e){
@@ -323,6 +217,38 @@ 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);
@@ -339,14 +265,15 @@ namespace TweetDuck.Core{
} }
} }
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){ private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
FormManager.CloseAllDialogs(); FormManager.CloseAllDialogs();
if (!string.IsNullOrEmpty(Config.DismissedUpdate)){
Config.DismissedUpdate = null;
Config.Save();
}
updates.BeginUpdateDownload(this, e.UpdateInfo, update => { updates.BeginUpdateDownload(this, e.UpdateInfo, update => {
if (update.DownloadStatus == UpdateDownloadStatus.Done){ if (update.DownloadStatus == UpdateDownloadStatus.Done){
UpdateInstallerPath = update.InstallerPath; UpdateInstallerPath = update.InstallerPath;
@@ -381,31 +308,21 @@ namespace TweetDuck.Core{
} }
protected override void WndProc(ref Message m){ protected override void WndProc(ref Message m){
if (isLoaded){ if (isLoaded && m.Msg == Program.WindowRestoreMessage){
if (m.Msg == Program.WindowRestoreMessage){ if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){
if (WindowsUtils.CurrentProcessID == m.WParam.ToInt32()){ trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
trayIcon_ClickRestore(trayIcon, EventArgs.Empty);
}
return;
} }
else if (m.Msg == Program.SubProcessMessage){
int processId = m.WParam.ToInt32();
if (WindowsUtils.IsChildProcess(processId)){ // child process is checked in two places for safety return;
BrowserProcesses.Link(m.LParam.ToInt32(), processId);
}
return;
}
} }
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){ if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
if (videoPlayer != null && videoPlayer.Running){ if (videoPlayer != null && videoPlayer.Running){
videoPlayer.Close(); videoPlayer.Close();
} }
else{ else{
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF); browser.OnMouseClickExtra(m.WParam);
TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserExtraMouseButton);
} }
return; return;
@@ -414,11 +331,7 @@ namespace TweetDuck.Core{
base.WndProc(ref m); base.WndProc(ref m);
} }
// notification helpers // bridge methods
public FormNotificationMain CreateNotificationForm(bool enableContextMenu){
return new FormNotificationMain(this, plugins, enableContextMenu);
}
public void PauseNotification(){ public void PauseNotification(){
notification.PauseNotification(); notification.PauseNotification();
@@ -428,22 +341,28 @@ namespace TweetDuck.Core{
notification.ResumeNotification(); notification.ResumeNotification();
} }
// javascript calls
public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
}
public void ReinjectCustomCSS(string css){ public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty); browser.ReinjectCustomCSS(css);
}
public void UpdateProperties(PropertyBridge.Environment environment){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
} }
public void ReloadToTweetDeck(){ public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'"); browser.ReloadToTweetDeck();
}
public void TriggerTweetScreenshot(){
browser.TriggerTweetScreenshot();
}
public void ReloadColumns(){
browser.ReloadColumns();
}
public void ApplyROT13(){
browser.ApplyROT13();
}
public void TriggerAnalyticsEvent(AnalyticsFile.Event e){
analytics?.TriggerEvent(e);
} }
// callback handlers // callback handlers
@@ -452,11 +371,16 @@ 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){
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
}
} }
if (showGuide){ if (showGuide){
ShowChildForm(new FormGuide()); FormGuide.Show();
} }
} }
@@ -472,7 +396,7 @@ namespace TweetDuck.Core{
if (!FormManager.TryBringToFront<FormSettings>()){ if (!FormManager.TryBringToFront<FormSettings>()){
bool prevEnableUpdateCheck = Config.EnableUpdateCheck; bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
FormSettings form = new FormSettings(this, plugins, updates, startTab); FormSettings form = new FormSettings(this, plugins, updates, analytics, startTab);
form.FormClosed += (sender, args) => { form.FormClosed += (sender, args) => {
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){ if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
@@ -486,37 +410,45 @@ namespace TweetDuck.Core{
trayIcon.HasNotifications = false; trayIcon.HasNotifications = false;
} }
if (Program.SystemConfig.EnableBrowserGCReload){ if (Config.AllowDataCollection){
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold); if (analytics == null){
analytics = new AnalyticsManager(this, plugins, Program.AnalyticsFilePath);
}
} }
else{ else if (analytics != null){
memoryUsageTracker.Stop(); analytics.Dispose();
analytics = null;
} }
BrowserCache.RefreshTimer();
if (form.ShouldReloadBrowser){ if (form.ShouldReloadBrowser){
FormManager.TryFind<FormPlugins>()?.Close(); FormManager.TryFind<FormPlugins>()?.Close();
plugins.Reload(); // also reloads the browser plugins.Reload(); // also reloads the browser
} }
else{ else{
UpdateProperties(PropertyBridge.Environment.Browser); browser.UpdateProperties();
} }
notification.RequiresResize = true; notification.RequiresResize = true;
form.Dispose(); form.Dispose();
}; };
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenOptions);
ShowChildForm(form); ShowChildForm(form);
} }
} }
public void OpenAbout(){ public void OpenAbout(){
if (!FormManager.TryBringToFront<FormAbout>()){ if (!FormManager.TryBringToFront<FormAbout>()){
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenAbout);
ShowChildForm(new FormAbout()); ShowChildForm(new FormAbout());
} }
} }
public void OpenPlugins(){ public void OpenPlugins(){
if (!FormManager.TryBringToFront<FormPlugins>()){ if (!FormManager.TryBringToFront<FormPlugins>()){
TriggerAnalyticsEvent(AnalyticsFile.Event.OpenPlugins);
ShowChildForm(new FormPlugins(plugins)); ShowChildForm(new FormPlugins(plugins));
} }
} }
@@ -539,9 +471,11 @@ namespace TweetDuck.Core{
soundNotification.SetVolume(Config.NotificationSoundVolume); soundNotification.SetVolume(Config.NotificationSoundVolume);
soundNotification.Play(Config.NotificationSoundPath); soundNotification.Play(Config.NotificationSoundPath);
TriggerAnalyticsEvent(AnalyticsFile.Event.SoundNotification);
} }
public void PlayVideo(string url){ public void PlayVideo(string url, string username){
if (string.IsNullOrEmpty(url)){ if (string.IsNullOrEmpty(url)){
videoPlayer?.Close(); videoPlayer?.Close();
return; return;
@@ -551,16 +485,12 @@ namespace TweetDuck.Core{
videoPlayer = new VideoPlayer(this); videoPlayer = new VideoPlayer(this);
videoPlayer.ProcessExited += (sender, args) => { videoPlayer.ProcessExited += (sender, args) => {
browser.GetBrowser().GetHost().SendFocusEvent(true); browser.HideVideoOverlay(true);
HideVideoOverlay();
}; };
} }
videoPlayer.Launch(url); videoPlayer.Launch(url, username);
} TriggerAnalyticsEvent(AnalyticsFile.Event.VideoPlay);
public void HideVideoOverlay(){
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
} }
public bool ProcessBrowserKey(Keys key){ public bool ProcessBrowserKey(Keys key){
@@ -575,15 +505,14 @@ namespace TweetDuck.Core{
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){ public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
Activate(); Activate();
using(IFrame frame = browser.GetBrowser().MainFrame){ if (!browser.IsTweetDeckWebsite){
if (!TwitterUtils.IsTweetDeckWebsite(frame)){ FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK); return;
return;
}
} }
notification.FinishCurrentNotification(); notification.FinishCurrentNotification();
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl); browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetDetail);
} }
public void OnTweetScreenshotReady(string html, int width, int height){ public void OnTweetScreenshotReady(string html, int width, int height){
@@ -592,6 +521,7 @@ namespace TweetDuck.Core{
} }
notificationScreenshotManager.Trigger(html, width, height); notificationScreenshotManager.Trigger(html, width, height);
TriggerAnalyticsEvent(AnalyticsFile.Event.TweetScreenshot);
} }
public void DisplayTooltip(string text){ public void DisplayTooltip(string text){
@@ -604,9 +534,5 @@ namespace TweetDuck.Core{
toolTip.Show(text, this, position); toolTip.Show(text, this, position);
} }
} }
public void TriggerTweetScreenshot(){
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
}
} }
} }

View File

@@ -8,10 +8,11 @@ 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.Other;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{ abstract class ContextMenuBase : IContextMenuHandler{
protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality; private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
@@ -31,10 +32,11 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500; private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500;
private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501; private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501;
private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502; private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502;
private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26503; private const CefMenuCommand MenuViewImage = (CefMenuCommand)26503;
private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504; private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26504;
private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505; private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505;
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506; private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506;
private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599;
private string[] lastHighlightedTweetAuthors; private string[] lastHighlightedTweetAuthors;
@@ -79,6 +81,7 @@ namespace TweetDuck.Core.Handling{
model.AddSeparator(); model.AddSeparator();
} }
else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){ else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){
model.AddItem(MenuViewImage, "View image in photo viewer");
model.AddItem(MenuOpenMediaUrl, TextOpen("image")); model.AddItem(MenuOpenMediaUrl, TextOpen("image"));
model.AddItem(MenuCopyMediaUrl, TextCopy("image")); model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
model.AddItem(MenuSaveMedia, TextSave("image")); model.AddItem(MenuSaveMedia, TextSave("image"));
@@ -114,9 +117,35 @@ namespace TweetDuck.Core.Handling{
SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality)); SetClipboardText(browserControl.AsControl(), TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
break; break;
case MenuViewImage:
string url = GetMediaLink(parameters);
string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
void ViewFile(){
string ext = Path.GetExtension(file);
if (TwitterUtils.ValidImageExtensions.Contains(ext)){
WindowsUtils.OpenAssociatedProgram(file);
}
else{
FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
}
}
if (File.Exists(file)){
ViewFile();
}
else{
BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
});
}
break;
case MenuSaveMedia: case MenuSaveMedia:
if (IsVideo){ if (IsVideo){
TwitterUtils.DownloadVideo(GetMediaLink(parameters)); TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
} }
else{ else{
TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality); TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);

View File

@@ -2,6 +2,7 @@
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{
@@ -17,6 +18,7 @@ namespace TweetDuck.Core.Handling{
private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612;
private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613;
private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614; private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614;
private const CefMenuCommand MenuInputApplyROT13 = (CefMenuCommand)26615;
private const string TitleReloadBrowser = "Reload browser"; private const string TitleReloadBrowser = "Reload browser";
private const string TitleMuteNotifications = "Mute notifications"; private const string TitleMuteNotifications = "Mute notifications";
@@ -41,6 +43,11 @@ namespace TweetDuck.Core.Handling{
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){ if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
if (parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){
model.AddSeparator();
model.AddItem(MenuInputApplyROT13, "Apply ROT13");
}
model.AddSeparator(); model.AddSeparator();
} }
@@ -89,6 +96,8 @@ namespace TweetDuck.Core.Handling{
} }
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
form.InvokeAsyncSafe(() => form.TriggerAnalyticsEvent(AnalyticsFile.Event.BrowserContextMenu));
} }
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){
@@ -136,6 +145,10 @@ namespace TweetDuck.Core.Handling{
case MenuCopyQuotedTweetUrl: case MenuCopyQuotedTweetUrl:
SetClipboardText(form, lastHighlightedQuoteUrl); SetClipboardText(form, lastHighlightedQuoteUrl);
return true; return true;
case MenuInputApplyROT13:
form.InvokeAsyncSafe(form.ApplyROT13);
return true;
} }
return false; return false;
@@ -153,6 +166,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);
}; };
return menu; return menu;

View File

@@ -1,6 +1,7 @@
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{
@@ -48,13 +49,16 @@ namespace TweetDuck.Core.Handling{
} }
if (HasDevTools){ if (HasDevTools){
model.AddSeparator(); AddSeparator(model);
AddDebugMenuItems(model); AddDebugMenuItems(model);
} }
RemoveSeparatorIfLast(model); RemoveSeparatorIfLast(model);
form.InvokeAsyncSafe(() => form.ContextMenuOpen = true); form.InvokeAsyncSafe(() => {
form.ContextMenuOpen = true;
form.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationContextMenu);
});
} }
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){

View File

@@ -4,11 +4,18 @@ using CefSharp;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class DragHandlerBrowser : IDragHandler{ sealed class DragHandlerBrowser : IDragHandler{
public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){ public bool OnDragEnter(IWebBrowser browserControl, IBrowser browser, IDragData dragData, DragOperationsMask mask){
void TriggerDragStart(string type, string data = null){
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", type, data);
}
if (dragData.IsLink){ if (dragData.IsLink){
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", "link", dragData.LinkUrl); TriggerDragStart("link", dragData.LinkUrl);
}
else if (dragData.IsFragment){
TriggerDragStart("text", dragData.FragmentText.Trim());
} }
else{ else{
browserControl.ExecuteScriptAsync("window.TDGF_onGlobalDragStart", "unknown"); TriggerDragStart("unknown");
} }
return false; return false;

View File

@@ -1,14 +1,24 @@
using System; using System;
using System.Threading.Tasks;
using CefSharp; using CefSharp;
namespace TweetDuck.Core.Handling.General{ namespace TweetDuck.Core.Handling.General{
sealed class BrowserProcessHandler : IBrowserProcessHandler{ sealed class BrowserProcessHandler : IBrowserProcessHandler{
void IBrowserProcessHandler.OnContextInitialized(){ public static Task UpdatePrefs(){
return Cef.UIThreadTaskFactory.StartNew(UpdatePrefsInternal);
}
private static void UpdatePrefsInternal(){
using(IRequestContext ctx = Cef.GetGlobalRequestContext()){ using(IRequestContext ctx = Cef.GetGlobalRequestContext()){
ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _); ctx.SetPreference("browser.enable_spellchecking", Program.UserConfig.EnableSpellCheck, out string _);
ctx.SetPreference("settings.a11y.animation_policy", Program.UserConfig.EnableAnimatedImages ? "allowed" : "none", out string _);
} }
} }
void IBrowserProcessHandler.OnContextInitialized(){
UpdatePrefsInternal();
}
void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){} void IBrowserProcessHandler.OnScheduleMessagePumpWork(long delay){}
void IDisposable.Dispose(){} void IDisposable.Dispose(){}
} }

View File

@@ -4,9 +4,7 @@ using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Handling.General{ namespace TweetDuck.Core.Handling.General{
sealed class LifeSpanHandler : ILifeSpanHandler{ sealed class LifeSpanHandler : ILifeSpanHandler{
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){ public static bool HandleLinkClick(IWebBrowser browserControl, WindowOpenDisposition targetDisposition, string targetUrl){
newBrowser = null;
switch(targetDisposition){ switch(targetDisposition){
case WindowOpenDisposition.NewBackgroundTab: case WindowOpenDisposition.NewBackgroundTab:
case WindowOpenDisposition.NewForegroundTab: case WindowOpenDisposition.NewForegroundTab:
@@ -20,6 +18,11 @@ namespace TweetDuck.Core.Handling.General{
} }
} }
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
newBrowser = null;
return HandleLinkClick(browserControl, targetDisposition, targetUrl);
}
public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){} public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){}
public bool DoClose(IWebBrowser browserControl, IBrowser browser){ public bool DoClose(IWebBrowser browserControl, IBrowser browser){

View File

@@ -1,68 +0,0 @@
using System.Security.Cryptography.X509Certificates;
using CefSharp;
namespace TweetDuck.Core.Handling.General{
abstract class RequestHandlerBase : IRequestHandler{
// Browser
public virtual void OnRenderViewReady(IWebBrowser browserControl, IBrowser browser){}
public virtual void OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser, CefTerminationStatus status){}
public virtual bool OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, bool isRedirect){
return false;
}
public virtual bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
return false;
}
public virtual bool OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url){
return false;
}
// Resources
public virtual CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback){
return CefReturnValue.Continue;
}
public virtual void OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, ref string newUrl){}
public virtual bool OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
return false;
}
public virtual IResponseFilter GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response){
return null;
}
public virtual void OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength){}
// JavaScript & Plugins
public virtual bool OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl, long newSize, IRequestCallback callback){
callback.Dispose();
return false;
}
public virtual void OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath){}
// Auth
public virtual bool GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback){
callback.Dispose();
return false;
}
public virtual bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback){
callback.Dispose();
return false;
}
public virtual bool OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback){
callback.Dispose();
return false;
}
}
}

View File

@@ -2,6 +2,7 @@
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{
@@ -11,19 +12,26 @@ namespace TweetDuck.Core.Handling {
this.notification = notification; this.notification = notification;
} }
private void TriggerKeyboardShortcutAnalytics(){
notification.InvokeAsyncSafe(() => notification.TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationKeyboardShortcut));
}
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){
if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){ if (type == KeyType.RawKeyDown && !browser.FocusedFrame.Url.StartsWith("chrome-devtools://")){
switch((Keys)windowsKeyCode){ switch((Keys)windowsKeyCode){
case Keys.Enter: case Keys.Enter:
notification.InvokeAsyncSafe(notification.FinishCurrentNotification); notification.InvokeAsyncSafe(notification.FinishCurrentNotification);
TriggerKeyboardShortcutAnalytics();
return true; return true;
case Keys.Escape: case Keys.Escape:
notification.InvokeAsyncSafe(() => notification.HideNotification(true)); notification.InvokeAsyncSafe(notification.HideNotification);
TriggerKeyboardShortcutAnalytics();
return true; return true;
case Keys.Space: case Keys.Space:
notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer); notification.InvokeAsyncSafe(() => notification.FreezeTimer = !notification.FreezeTimer);
TriggerKeyboardShortcutAnalytics();
return true; return true;
} }
} }

View File

@@ -0,0 +1,11 @@
using CefSharp;
using CefSharp.Handler;
using TweetDuck.Core.Handling.General;
namespace TweetDuck.Core.Handling{
class RequestHandlerBase : DefaultRequestHandler{
public override bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture){
return LifeSpanHandler.HandleLinkClick(browserControl, targetDisposition, targetUrl);
}
}
}

View File

@@ -1,5 +1,4 @@
using CefSharp; using CefSharp;
using TweetDuck.Core.Handling.General;
namespace TweetDuck.Core.Handling{ namespace TweetDuck.Core.Handling{
sealed class RequestHandlerBrowser : RequestHandlerBase{ sealed class RequestHandlerBrowser : RequestHandlerBase{

View File

@@ -0,0 +1,46 @@
using System.Windows.Forms;
using TweetDuck.Plugins;
using TweetDuck.Resources;
namespace TweetDuck.Core.Notification.Example{
sealed class FormNotificationExample : FormNotificationMain{
public override bool RequiresResize => true;
protected override bool CanDragWindow => Program.UserConfig.NotificationPosition == TweetNotification.Position.Custom;
protected override FormBorderStyle NotificationBorderStyle{
get{
if (Program.UserConfig.NotificationSize == TweetNotification.Size.Custom){
switch(base.NotificationBorderStyle){
case FormBorderStyle.FixedSingle: return FormBorderStyle.Sizable;
case FormBorderStyle.FixedToolWindow: return FormBorderStyle.SizableToolWindow;
}
}
return base.NotificationBorderStyle;
}
}
private readonly TweetNotification exampleNotification;
public FormNotificationExample(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, false){
string exampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
#if DEBUG
exampleTweetHTML = exampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
#endif
exampleNotification = TweetNotification.Example(exampleTweetHTML, 95);
}
public void ShowExampleNotification(bool reset){
if (reset){
LoadTweet(exampleNotification);
}
else{
PrepareAndDisplayWindow();
}
UpdateTitle();
}
}
}

View File

@@ -8,14 +8,14 @@ using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling; using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General; using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Management; 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{
protected static int FontSizeLevel{ protected static int FontSizeLevel{
get{ get{
switch(TweetDeckBridge.FontSizeClass){ switch(TweetDeckBridge.FontSize){
case "largest": return 4; case "largest": return 4;
case "large": return 3; case "large": return 3;
case "small": return 1; case "small": return 1;
@@ -25,7 +25,7 @@ namespace TweetDuck.Core.Notification{
} }
} }
protected Point PrimaryLocation{ protected virtual Point PrimaryLocation{
get{ get{
UserConfig config = Program.UserConfig; UserConfig config = Program.UserConfig;
Screen screen; Screen screen;
@@ -66,31 +66,39 @@ namespace TweetDuck.Core.Notification{
} }
public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation; public bool IsNotificationVisible => Location != ControlExtensions.InvisibleLocation;
protected virtual bool CanDragWindow => true;
public new Point Location{ public new Point Location{
get => base.Location; get{
return base.Location;
}
set{ set{
Visible = (base.Location = value) != ControlExtensions.InvisibleLocation; Visible = (base.Location = value) != ControlExtensions.InvisibleLocation;
FormBorderStyle = GetBorderStyle(CanResizeWindow); FormBorderStyle = NotificationBorderStyle;
} }
} }
public bool CanResizeWindow{ protected virtual FormBorderStyle NotificationBorderStyle{
get => FormBorderStyle == FormBorderStyle.Sizable || FormBorderStyle == FormBorderStyle.SizableToolWindow; get{
set => FormBorderStyle = GetBorderStyle(value); if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
return FormBorderStyle.FixedSingle;
}
else{
return FormBorderStyle.FixedToolWindow;
}
}
} }
public Func<bool> CanMoveWindow { get; set; } = () => true;
protected override bool ShowWithoutActivation => true; protected override bool ShowWithoutActivation => true;
protected double SizeScale => dpiScale*Program.UserConfig.ZoomMultiplier; protected float DpiScale { get; }
protected double SizeScale => DpiScale*Program.UserConfig.ZoomLevel/100.0;
protected readonly FormBrowser owner; protected readonly FormBrowser owner;
protected readonly ChromiumWebBrowser browser; protected readonly ChromiumWebBrowser browser;
private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification(); private readonly ResourceHandlerNotification resourceHandler = new ResourceHandlerNotification();
private readonly float dpiScale;
private TweetNotification currentNotification; private TweetNotification currentNotification;
private int pauseCounter; private int pauseCounter;
@@ -101,6 +109,8 @@ namespace TweetDuck.Core.Notification{
public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId); public bool CanViewDetail => currentNotification != null && !string.IsNullOrEmpty(currentNotification.ColumnId) && !string.IsNullOrEmpty(currentNotification.ChirpId);
public bool IsPaused => pauseCounter > 0; public bool IsPaused => pauseCounter > 0;
protected bool IsCursorOverBrowser => browser.Bounds.Contains(PointToClient(Cursor.Position));
public bool FreezeTimer { get; set; } public bool FreezeTimer { get; set; }
public bool ContextMenuOpen { get; set; } public bool ContextMenuOpen { get; set; }
@@ -115,18 +125,19 @@ namespace TweetDuck.Core.Notification{
this.browser = new ChromiumWebBrowser("about:blank"){ this.browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this, enableContextMenu), MenuHandler = new ContextMenuNotification(this, enableContextMenu),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler() LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBase()
}; };
this.browser.Dock = DockStyle.None; this.browser.Dock = DockStyle.None;
this.browser.ClientSize = ClientSize; this.browser.ClientSize = ClientSize;
this.browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged; this.browser.IsBrowserInitializedChanged += browser_IsBrowserInitializedChanged;
#if DEBUG #if DEBUG
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage; this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif #endif
this.dpiScale = this.GetDPIScale(); DpiScale = this.GetDPIScale();
DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory; DefaultResourceHandlerFactory handlerFactory = (DefaultResourceHandlerFactory)browser.ResourceHandlerFactory;
handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler); handlerFactory.RegisterHandler(TwitterUtils.TweetDeckURL, this.resourceHandler);
@@ -143,35 +154,33 @@ namespace TweetDuck.Core.Notification{
} }
protected override void WndProc(ref Message m){ protected override void WndProc(ref Message m){
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanDragWindow){ // WM_SYSCOMMAND, SC_MOVE
return; return;
} }
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){
Close(); Close();
} }
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){ private void browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized){ if (e.IsBrowserInitialized){
Initialized?.Invoke(this, EventArgs.Empty); Initialized?.Invoke(this, EventArgs.Empty);
int identifier = browser.GetBrowser().Identifier;
Disposed += (sender2, args2) => BrowserProcesses.Forget(identifier);
} }
} }
// notification methods // notification methods
public virtual void HideNotification(bool loadBlank){ public virtual void HideNotification(){
if (loadBlank){ browser.Load("about:blank");
browser.Load("about:blank");
}
Location = ControlExtensions.InvisibleLocation; Location = ControlExtensions.InvisibleLocation;
currentNotification = null; currentNotification = null;
} }
@@ -191,8 +200,7 @@ namespace TweetDuck.Core.Notification{
} }
protected virtual string GetTweetHTML(TweetNotification tweet){ protected virtual string GetTweetHTML(TweetNotification tweet){
string bodyClasses = browser.Bounds.Contains(PointToClient(Cursor.Position)) ? "td-hover" : string.Empty; return tweet.GenerateHtml(IsCursorOverBrowser ? "td-hover" : string.Empty);
return tweet.GenerateHtml(bodyClasses);
} }
protected virtual void LoadTweet(TweetNotification tweet){ protected virtual void LoadTweet(TweetNotification tweet){
@@ -233,14 +241,5 @@ namespace TweetDuck.Core.Notification{
toolTip.Show(text, this, position); toolTip.Show(text, this, position);
} }
} }
private FormBorderStyle GetBorderStyle(bool sizable){
if (WindowsUtils.ShouldAvoidToolWindow && Visible){ // Visible = workaround for alt+tab
return sizable ? FormBorderStyle.Sizable : FormBorderStyle.FixedSingle;
}
else{
return sizable ? FormBorderStyle.SizableToolWindow : FormBorderStyle.FixedToolWindow;
}
}
} }
} }

View File

@@ -49,7 +49,7 @@
this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0); this.progressBarTimer.Margin = new System.Windows.Forms.Padding(0);
this.progressBarTimer.Maximum = 1000; this.progressBarTimer.Maximum = 1000;
this.progressBarTimer.Name = "progressBarTimer"; this.progressBarTimer.Name = "progressBarTimer";
this.progressBarTimer.Size = new System.Drawing.Size(284, TimerBarHeight); this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
this.progressBarTimer.TabIndex = 1; this.progressBarTimer.TabIndex = 1;
// //
// FormNotification // FormNotification

View File

@@ -5,6 +5,7 @@ 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;
@@ -12,14 +13,14 @@ using TweetDuck.Plugins.Enums;
using TweetDuck.Resources; using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
partial class FormNotificationMain : FormNotificationBase{ abstract partial class FormNotificationMain : FormNotificationBase{
private const string NotificationScriptFile = "notification.js"; private const string NotificationScriptFile = "notification.js";
private const int TimerBarHeight = 4;
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile); private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile); private static readonly string NotificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
private readonly PluginManager plugins; private readonly PluginManager plugins;
private readonly int timerBarHeight;
protected int timeLeft, totalTime; protected int timeLeft, totalTime;
protected bool pausedDuringNotification; protected bool pausedDuringNotification;
@@ -31,9 +32,9 @@ namespace TweetDuck.Core.Notification{
private bool? prevDisplayTimer; private bool? prevDisplayTimer;
private int? prevFontSize; private int? prevFontSize;
public bool RequiresResize{ public virtual bool RequiresResize{
get{ get{
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel || CanResizeWindow; return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != FontSizeLevel;
} }
set{ set{
@@ -72,14 +73,13 @@ namespace TweetDuck.Core.Notification{
} }
} }
public Size BrowserSize{ public Size BrowserSize => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-timerBarHeight) : ClientSize;
get => Program.UserConfig.DisplayNotificationTimer ? new Size(ClientSize.Width, ClientSize.Height-TimerBarHeight) : ClientSize;
}
public FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){ protected FormNotificationMain(FormBrowser owner, PluginManager pluginManager, bool enableContextMenu) : base(owner, enableContextMenu){
InitializeComponent(); InitializeComponent();
this.plugins = pluginManager; this.plugins = pluginManager;
this.timerBarHeight = BrowserUtils.Scale(4, DpiScale);
browser.KeyboardHandler = new KeyboardHandlerNotification(this); browser.KeyboardHandler = new KeyboardHandlerNotification(this);
@@ -113,8 +113,8 @@ namespace TweetDuck.Core.Notification{
if (nCode == 0){ if (nCode == 0){
int eventType = wParam.ToInt32(); int eventType = wParam.ToInt32();
if (eventType == NativeMethods.WM_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){ if (eventType == NativeMethods.WM_MOUSEWHEEL && IsCursorOverBrowser){
browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed/100.0), CefEventFlags.None); browser.SendMouseWheelEvent(0, 0, 0, BrowserUtils.Scale(NativeMethods.GetMouseHookData(lParam), Program.UserConfig.NotificationScrollSpeed*0.01), CefEventFlags.None);
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){ else if (eventType == NativeMethods.WM_XBUTTONDOWN && DesktopBounds.Contains(Cursor.Position)){
@@ -128,6 +128,7 @@ namespace TweetDuck.Core.Notification{
} }
blockXButtonUp = true; blockXButtonUp = true;
this.InvokeAsyncSafe(() => TriggerAnalyticsEvent(AnalyticsFile.Event.NotificationExtraMouseButton));
return NativeMethods.HOOK_HANDLED; return NativeMethods.HOOK_HANDLED;
} }
else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){ else if (eventType == NativeMethods.WM_XBUTTONUP && blockXButtonUp){
@@ -148,7 +149,7 @@ namespace TweetDuck.Core.Notification{
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){ private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
if (e.CloseReason == CloseReason.UserClosing){ if (e.CloseReason == CloseReason.UserClosing){
HideNotification(true); HideNotification();
e.Cancel = true; e.Cancel = true;
} }
} }
@@ -176,12 +177,14 @@ namespace TweetDuck.Core.Notification{
} }
private void timerHideProgress_Tick(object sender, EventArgs e){ private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return; if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen){
return;
}
timeLeft -= timerProgress.Interval; timeLeft -= timerProgress.Interval;
int value = BrowserUtils.Scale(1025, (totalTime-timeLeft)/(double)totalTime); int value = BrowserUtils.Scale(progressBarTimer.Maximum+25, (totalTime-timeLeft)/(double)totalTime);
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value))); progressBarTimer.SetValueInstant(Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum-value : value);
if (timeLeft <= 0){ if (timeLeft <= 0){
FinishCurrentNotification(); FinishCurrentNotification();
@@ -194,21 +197,10 @@ namespace TweetDuck.Core.Notification{
LoadTweet(notification); LoadTweet(notification);
} }
public void ShowNotificationForSettings(bool reset){ public override void HideNotification(){
if (reset){ base.HideNotification();
LoadTweet(TweetNotification.ExampleTweet);
}
else{
PrepareAndDisplayWindow();
}
UpdateTitle(); progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
}
public override void HideNotification(bool loadBlank){
base.HideNotification(loadBlank);
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
timerProgress.Stop(); timerProgress.Stop();
totalTime = 0; totalTime = 0;
@@ -251,14 +243,14 @@ namespace TweetDuck.Core.Notification{
protected override void LoadTweet(TweetNotification tweet){ protected override void LoadTweet(TweetNotification tweet){
timerProgress.Stop(); timerProgress.Stop();
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue); totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0; progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? progressBarTimer.Maximum : progressBarTimer.Minimum;
base.LoadTweet(tweet); base.LoadTweet(tweet);
} }
protected override void SetNotificationSize(int width, int height){ protected override void SetNotificationSize(int width, int height){
if (Program.UserConfig.DisplayNotificationTimer){ if (Program.UserConfig.DisplayNotificationTimer){
ClientSize = new Size(width, height+TimerBarHeight); ClientSize = new Size(width, height+timerBarHeight);
progressBarTimer.Visible = true; progressBarTimer.Visible = true;
} }
else{ else{
@@ -269,7 +261,7 @@ namespace TweetDuck.Core.Notification{
browser.ClientSize = new Size(width, height); browser.ClientSize = new Size(width, height);
} }
private void PrepareAndDisplayWindow(){ protected void PrepareAndDisplayWindow(){
if (RequiresResize){ if (RequiresResize){
RequiresResize = false; RequiresResize = false;
SetNotificationSize(BaseClientWidth, BaseClientHeight); SetNotificationSize(BaseClientWidth, BaseClientHeight);

View File

@@ -40,7 +40,6 @@ namespace TweetDuck.Core.Notification {
// //
// FormNotificationTweet // FormNotificationTweet
// //
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotificationTweet_FormClosing);
this.ResumeLayout(true); this.ResumeLayout(true);
} }

View File

@@ -3,6 +3,7 @@ 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{
@@ -10,10 +11,24 @@ namespace TweetDuck.Core.Notification{
private const int NonIntrusiveIdleLimit = 30; private const int NonIntrusiveIdleLimit = 30;
private const int TrimMinimum = 32; private const int TrimMinimum = 32;
protected override Point PrimaryLocation => hasTemporarilyMoved && IsNotificationVisible ? Location : base.PrimaryLocation;
private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position); private bool IsCursorOverNotificationArea => new Rectangle(PrimaryLocation, Size).Contains(Cursor.Position);
protected override bool CanDragWindow{
get{
if (ModifierKeys.HasFlag(Keys.Alt)){
hasTemporarilyMoved = true;
return true;
}
else{
return false;
}
}
}
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4); private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
private bool needsTrim; private bool needsTrim;
private bool hasTemporarilyMoved;
public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){ public FormNotificationTweet(FormBrowser owner, PluginManager pluginManager) : base(owner, pluginManager, true){
InitializeComponent(); InitializeComponent();
@@ -26,18 +41,18 @@ namespace TweetDuck.Core.Notification{
} }
} }
private void FormNotificationTweet_FormClosing(object sender, FormClosingEventArgs e){ protected override void WndProc(ref Message m){
if (e.CloseReason == CloseReason.UserClosing){ if (m.Msg == 0x00A7){ // WM_NCMBUTTONDOWN
tweetQueue.Clear(); // already canceled int hitTest = m.WParam.ToInt32();
TrimQueue();
}
}
private void TrimQueue(){ if (hitTest == 2 || hitTest == 20){ // HTCAPTION, HTCLOSE
if (needsTrim){ hasTemporarilyMoved = false;
tweetQueue.TrimExcess(); MoveToVisibleLocation();
needsTrim = false; return;
}
} }
base.WndProc(ref m);
} }
// event handlers // event handlers
@@ -68,11 +83,9 @@ namespace TweetDuck.Core.Notification{
// notification methods // notification methods
public override void ShowNotification(TweetNotification notification){ public override void ShowNotification(TweetNotification notification){
if (IsPaused){ tweetQueue.Enqueue(notification);
tweetQueue.Enqueue(notification);
} if (!IsPaused){
else{
tweetQueue.Enqueue(notification);
UpdateTitle(); UpdateTitle();
if (totalTime == 0){ if (totalTime == 0){
@@ -81,6 +94,19 @@ namespace TweetDuck.Core.Notification{
} }
needsTrim |= tweetQueue.Count >= TrimMinimum; needsTrim |= tweetQueue.Count >= TrimMinimum;
TriggerAnalyticsEvent(AnalyticsFile.Event.DesktopNotification);
}
public override void HideNotification(){
base.HideNotification();
tweetQueue.Clear();
if (needsTrim){
tweetQueue.TrimExcess();
needsTrim = false;
}
hasTemporarilyMoved = false;
} }
public override void FinishCurrentNotification(){ public override void FinishCurrentNotification(){
@@ -88,8 +114,7 @@ namespace TweetDuck.Core.Notification{
LoadNextNotification(); LoadNextNotification();
} }
else{ else{
HideNotification(true); HideNotification();
TrimQueue();
} }
} }

View File

@@ -12,6 +12,8 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification.Screenshot{ namespace TweetDuck.Core.Notification.Screenshot{
sealed class FormNotificationScreenshotable : FormNotificationBase{ sealed class FormNotificationScreenshotable : FormNotificationBase{
protected override bool CanDragWindow => false;
private readonly PluginManager plugins; private readonly PluginManager plugins;
public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){ public FormNotificationScreenshotable(Action callback, FormBrowser owner, PluginManager pluginManager) : base(owner, false){
@@ -22,7 +24,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
browser.LoadingStateChanged += (sender, args) => { browser.LoadingStateChanged += (sender, args) => {
if (!args.IsLoading){ if (!args.IsLoading){
using(IFrame frame = args.Browser.MainFrame){ using(IFrame frame = args.Browser.MainFrame){
ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 129)", "gen:screenshot"); ScriptLoader.ExecuteScript(frame, "window.setTimeout($TD_NotificationScreenshot.trigger, document.getElementsByTagName('iframe').length ? 267 : 67)", "gen:screenshot");
} }
} }
}; };

View File

@@ -42,10 +42,7 @@ namespace TweetDuck.Core.Notification.Screenshot{
return; return;
} }
screenshot = new FormNotificationScreenshotable(Callback, owner, plugins){ screenshot = new FormNotificationScreenshotable(Callback, owner, plugins);
CanMoveWindow = () => false
};
screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height); screenshot.LoadNotificationForScreenshot(new TweetNotification(string.Empty, string.Empty, string.Empty, html, 0, string.Empty, string.Empty), width, height);
screenshot.Show(); screenshot.Show();
timeout.Start(); timeout.Start();

View File

@@ -5,25 +5,11 @@ using TweetDuck.Resources;
namespace TweetDuck.Core.Notification{ namespace TweetDuck.Core.Notification{
sealed class TweetNotification{ sealed class TweetNotification{
private const string DefaultFontSizeClass = "medium"; 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 DefaultHeadContents = @"<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");
private static string ExampleTweetHTML; public static TweetNotification Example(string html, int characters){
return new TweetNotification(string.Empty, string.Empty, "Home", html, characters, string.Empty, string.Empty, true);
public static TweetNotification ExampleTweet{
get{
if (ExampleTweetHTML == null){
ExampleTweetHTML = ScriptLoader.LoadResource("pages/example.html", true);
#if DEBUG
ExampleTweetHTML = ExampleTweetHTML.Replace("</p>", @"</p><div style='margin-top:256px'>Scrollbar test padding...</div>");
#endif
}
return new TweetNotification(string.Empty, string.Empty, "Home", ExampleTweetHTML, 95, string.Empty, string.Empty, true);
}
} }
public enum Position{ public enum Position{
@@ -67,8 +53,7 @@ namespace TweetDuck.Core.Notification{
public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){ public string GenerateHtml(string bodyClasses = null, bool enableCustomCSS = true){
StringBuilder build = new StringBuilder(); StringBuilder build = new StringBuilder();
build.Append("<!DOCTYPE html>"); build.Append("<!DOCTYPE html>");
build.Append("<html class='os-windows txt-base-").Append(TweetDeckBridge.FontSizeClass ?? DefaultFontSizeClass).Append("'>"); build.Append(TweetDeckBridge.NotificationHeadLayout ?? DefaultHeadLayout);
build.Append("<head>").Append(TweetDeckBridge.NotificationHeadContents ?? DefaultHeadContents);
if (enableCustomCSS){ if (enableCustomCSS){
build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>"); build.Append("<style type='text/css'>").Append(CustomCSS).Append("</style>");
@@ -85,7 +70,7 @@ namespace TweetDuck.Core.Notification{
build.Append(' ').Append(bodyClasses); build.Append(' ').Append(bodyClasses);
} }
build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%;height:auto;overflow:initial;'>"); build.Append('\'').Append(isExample ? " td-example-notification" : "").Append("><div class='column' style='width:100%!important;height:auto!important;overflow:initial!important;'>");
build.Append(html); build.Append(html);
build.Append("</div></body>"); build.Append("</div></body>");
build.Append("</html>"); build.Append("</html>");

View File

@@ -0,0 +1,103 @@
using System;
using TweetDuck.Data.Serialization;
namespace TweetDuck.Core.Other.Analytics{
sealed class AnalyticsFile{
private static readonly FileSerializer<AnalyticsFile> Serializer = new FileSerializer<AnalyticsFile>{
HandleUnknownProperties = FileSerializer<AnalyticsFile>.IgnoreProperties("CountGCReloads")
};
static AnalyticsFile(){
Serializer.RegisterTypeConverter(typeof(DateTime), new SingleTypeConverter<DateTime>{
ConvertToString = value => value.ToBinary().ToString(),
ConvertToObject = value => DateTime.FromBinary(long.Parse(value))
});
}
public enum Event{
OpenOptions, OpenPlugins, OpenAbout, OpenGuide,
DesktopNotification, SoundNotification, MuteNotification,
BrowserContextMenu, BrowserExtraMouseButton,
NotificationContextMenu, NotificationExtraMouseButton, NotificationKeyboardShortcut,
TweetScreenshot, TweetDetail, VideoPlay
}
// STATE PROPERTIES
public DateTime LastDataCollection { get; set; } = DateTime.MinValue;
public string LastCollectionVersion { get; set; } = null;
public string LastCollectionMessage { get; set; } = null;
// USAGE DATA
public int CountOpenOptions { get; private set; } = 0;
public int CountOpenPlugins { get; private set; } = 0;
public int CountOpenAbout { get; private set; } = 0;
public int CountOpenGuide { get; private set; } = 0;
public int CountDesktopNotifications { get; private set; } = 0;
public int CountSoundNotifications { get; private set; } = 0;
public int CountMuteNotifications { get; private set; } = 0;
public int CountBrowserContextMenus { get; private set; } = 0;
public int CountBrowserExtraMouseButtons { get; private set; } = 0;
public int CountNotificationContextMenus { get; private set; } = 0;
public int CountNotificationExtraMouseButtons { get; private set; } = 0;
public int CountNotificationKeyboardShortcuts { get; private set; } = 0;
public int CountTweetScreenshots { get; private set; } = 0;
public int CountTweetDetails { get; private set; } = 0;
public int CountVideoPlays { get; private set; } = 0;
// END OF DATA
private readonly string file;
private AnalyticsFile(string file){
this.file = file;
}
public void TriggerEvent(Event e){
switch(e){
case Event.OpenOptions: ++CountOpenOptions; break;
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 Save(){
try{
Serializer.Write(file, this);
}catch(Exception e){
Program.Reporter.HandleException("Analytics File Error", "Could not save the analytics file.", true, e);
}
}
public static AnalyticsFile Load(string file){
AnalyticsFile config = new AnalyticsFile(file);
try{
Serializer.ReadIfExists(file, config);
}catch(Exception e){
Program.Reporter.HandleException("Analytics File Error", "Could not open the analytics file.", true, e);
}
return config;
}
}
}

View File

@@ -0,0 +1,132 @@
using System;
using System.Net;
using System.Threading.Tasks;
using System.Timers;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Analytics{
sealed class AnalyticsManager : IDisposable{
private static readonly TimeSpan CollectionInterval = TimeSpan.FromDays(7);
private static readonly Uri CollectionUrl = new Uri("https://tweetduck.chylex.com/breadcrumb/report");
public AnalyticsFile File { get; }
private readonly FormBrowser browser;
private readonly PluginManager plugins;
private readonly Timer currentTimer, saveTimer;
public AnalyticsManager(FormBrowser browser, PluginManager plugins, string file){
this.browser = browser;
this.plugins = plugins;
this.File = AnalyticsFile.Load(file);
this.currentTimer = new Timer{ SynchronizingObject = browser };
this.currentTimer.Elapsed += currentTimer_Elapsed;
this.saveTimer = new Timer{ SynchronizingObject = browser, Interval = 60000 };
this.saveTimer.Elapsed += saveTimer_Elapsed;
if (this.File.LastCollectionVersion != Program.VersionTag){
ScheduleReportIn(TimeSpan.FromHours(12), string.Empty);
}
else{
RestartTimer();
}
}
public void Dispose(){
if (saveTimer.Enabled){
File.Save();
}
currentTimer.Dispose();
saveTimer.Dispose();
}
public void TriggerEvent(AnalyticsFile.Event e){
File.TriggerEvent(e);
saveTimer.Enabled = true;
}
private void saveTimer_Elapsed(object sender, ElapsedEventArgs e){
saveTimer.Stop();
File.Save();
}
private void ScheduleReportIn(TimeSpan delay, string message = null){
SetLastDataCollectionTime(DateTime.Now.Subtract(CollectionInterval).Add(delay), message);
}
private void SetLastDataCollectionTime(DateTime dt, string message = null){
File.LastDataCollection = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, dt.Kind);
File.LastCollectionVersion = Program.VersionTag;
File.LastCollectionMessage = message ?? dt.ToString("g", Program.Culture);
File.Save();
RestartTimer();
}
private void RestartTimer(){
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
int minutesTillNext = (int)(CollectionInterval.TotalMinutes-Math.Floor(diff.TotalMinutes));
currentTimer.Interval = Math.Max(minutesTillNext, 1)*60000;
currentTimer.Start();
}
private void currentTimer_Elapsed(object sender, ElapsedEventArgs e){
currentTimer.Stop();
TimeSpan diff = DateTime.Now.Subtract(File.LastDataCollection);
if (Math.Floor(diff.TotalMinutes) >= CollectionInterval.TotalMinutes){
SendReport();
}
else{
RestartTimer();
}
}
private void SendReport(){
AnalyticsReportGenerator.ExternalInfo info = AnalyticsReportGenerator.ExternalInfo.From(browser);
Task.Factory.StartNew(() => {
AnalyticsReport report = AnalyticsReportGenerator.Create(File, info, plugins);
#if DEBUG
System.Diagnostics.Debugger.Break();
#endif
BrowserUtils.CreateWebClient().UploadValues(CollectionUrl, "POST", report.ToNameValueCollection());
}).ContinueWith(task => browser.InvokeAsyncSafe(() => {
if (task.Status == TaskStatus.RanToCompletion){
SetLastDataCollectionTime(DateTime.Now);
}
else if (task.Exception != null){
string message = null;
if (task.Exception.InnerException is WebException e){
switch(e.Status){
case WebExceptionStatus.ConnectFailure:
message = "Connection Error";
break;
case WebExceptionStatus.NameResolutionFailure:
message = "DNS Error";
break;
case WebExceptionStatus.ProtocolError:
HttpWebResponse response = e.Response as HttpWebResponse;
message = "HTTP Error "+(response != null ? $"{(int)response.StatusCode} ({response.StatusDescription})" : "(unknown code)");
break;
}
}
ScheduleReportIn(TimeSpan.FromHours(4), message ?? "Error: "+(task.Exception.InnerException?.Message ?? task.Exception.Message));
}
}));
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Specialized;
using System.Text;
namespace TweetDuck.Core.Other.Analytics{
sealed class AnalyticsReport : IEnumerable{
private OrderedDictionary data = new OrderedDictionary(32);
private int separators;
public void Add(int ignored){ // adding separators to pretty print
data.Add((++separators).ToString(), null);
}
public void Add(string key, string value){
data.Add(key, value);
}
public AnalyticsReport FinalizeReport(){
if (!data.IsReadOnly){
data = data.AsReadOnly();
}
return this;
}
public IEnumerator GetEnumerator(){
return data.GetEnumerator();
}
public NameValueCollection ToNameValueCollection(){
NameValueCollection collection = new NameValueCollection();
foreach(DictionaryEntry entry in data){
if (entry.Value != null){
collection.Add(((string)entry.Key).ToLower().Replace(' ', '_'), (string)entry.Value);
}
}
return collection;
}
public override string ToString(){
StringBuilder build = new StringBuilder();
foreach(DictionaryEntry entry in data){
if (entry.Value == null){
build.AppendLine();
}
else{
build.AppendLine(entry.Key+": "+entry.Value);
}
}
return build.ToString();
}
}
}

View File

@@ -0,0 +1,303 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;
using TweetDuck.Configuration;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Analytics{
static class AnalyticsReportGenerator{
public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins){
Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;
return new AnalyticsReport{
{ "App Version" , Program.VersionTag },
{ "App Type" , Program.IsPortable ? "portable" : "installed" },
{ "App Dev Tools" , Bool(ContextMenuBase.HasDevTools) },
0,
{ "System Name" , SystemName },
{ "System Edition" , SystemEdition },
{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
{ "System Build" , SystemBuild },
{ "System Locale" , Program.Culture.Name.ToLower() },
0,
{ "RAM" , Exact(RamSize) },
{ "GPU" , GpuVendor },
0,
{ "Screen Count" , Exact(Screen.AllScreens.Length) },
{ "Screen Resolution" , info.Resolution ?? "(unknown)" },
{ "Screen DPI" , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
0,
{ "Hardware Acceleration" , Bool(SysConfig.HardwareAcceleration) },
0,
{ "Expand Links" , Bool(UserConfig.ExpandLinksOnHover) },
{ "Switch Account Selectors" , Bool(UserConfig.SwitchAccountSelectors) },
{ "Search In First Column" , Bool(UserConfig.OpenSearchInFirstColumn) },
{ "Best Image Quality" , Bool(UserConfig.BestImageQuality) },
{ "Spell Check" , Bool(UserConfig.EnableSpellCheck) },
{ "Zoom" , Exact(UserConfig.ZoomLevel) },
0,
{ "Updates" , Bool(UserConfig.EnableUpdateCheck) },
{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
0,
{ "Tray" , TrayMode },
{ "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
0,
{ "Notification Position" , NotificationPosition },
{ "Notification Size" , NotificationSize },
{ "Notification Timer" , NotificationTimer },
{ "Notification Timer Speed" , RoundUp(UserConfig.NotificationDurationValue, 5) },
{ "Notification Scroll Speed" , Exact(UserConfig.NotificationScrollSpeed) },
{ "Notification Column Title" , Bool(UserConfig.DisplayNotificationColumn) },
{ "Notification Media Previews" , Bool(UserConfig.NotificationMediaPreviews) },
{ "Notification Link Skip" , Bool(UserConfig.NotificationSkipOnLinkClick) },
{ "Notification Non-Intrusive" , Bool(UserConfig.NotificationNonIntrusiveMode) },
{ "Notification Idle Pause" , Exact(UserConfig.NotificationIdlePauseSeconds) },
{ "Custom Sound Notification" , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
{ "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) },
0,
{ "Program Arguments" , List(ProgramArguments) },
{ "Custom CEF Arguments" , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
{ "Custom Browser CSS" , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
0,
{ "Plugins All" , List(plugins.Plugins.Select(plugin => plugin.Identifier)) },
{ "Plugins Enabled" , List(plugins.Plugins.Where(plugin => plugins.Config.IsEnabled(plugin)).Select(plugin => plugin.Identifier)) },
0,
{ "Theme" , Dict(editLayoutDesign, "_theme", "light/def") },
{ "Column Width" , Dict(editLayoutDesign, "columnWidth", "310px/def") },
{ "Font Size" , Dict(editLayoutDesign, "fontSize", "12px/def") },
{ "Large Quote Font" , Dict(editLayoutDesign, "increaseQuoteTextSize", "false/def") },
{ "Small Compose Font" , Dict(editLayoutDesign, "smallComposeTextSize", "false/def") },
{ "Avatar Radius" , Dict(editLayoutDesign, "avatarRadius", "2/def") },
{ "Hide Tweet Actions" , Dict(editLayoutDesign, "hideTweetActions", "true/def") },
{ "Move Tweet Actions" , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
{ "Theme Color Tweaks" , Dict(editLayoutDesign, "themeColorTweaks", "true/def") },
{ "Revert Icons" , Dict(editLayoutDesign, "revertIcons", "true/def") },
{ "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations", "true/def") },
{ "Reply Account Mode" , ReplyAccountConfigFromPlugin },
{ "Template Count" , Exact(TemplateCountFromPlugin) },
0,
{ "Opened Options" , LogRound(file.CountOpenOptions, 4) },
{ "Opened Plugins" , LogRound(file.CountOpenPlugins, 4) },
{ "Opened About" , LogRound(file.CountOpenAbout, 4) },
{ "Opened Guide" , LogRound(file.CountOpenGuide, 4) },
{ "Desktop Notifications" , LogRound(file.CountDesktopNotifications, 5) },
{ "Sound Notifications" , LogRound(file.CountSoundNotifications, 5) },
{ "Mute Notifications" , LogRound(file.CountMuteNotifications, 2) },
{ "Browser Context Menus" , LogRound(file.CountBrowserContextMenus, 2) },
{ "Browser Extra Mouse Buttons" , LogRound(file.CountBrowserExtraMouseButtons, 2) },
{ "Notification Context Menus" , LogRound(file.CountNotificationContextMenus, 2) },
{ "Notification Extra Mouse Buttons" , LogRound(file.CountNotificationExtraMouseButtons, 2) },
{ "Notification Keyboard Shortcuts" , LogRound(file.CountNotificationKeyboardShortcuts, 2) },
{ "Tweet Screenshots" , LogRound(file.CountTweetScreenshots, 2) },
{ "Tweet Details" , LogRound(file.CountTweetDetails, 2) },
{ "Video Plays" , LogRound(file.CountVideoPlays, 4) }
}.FinalizeReport();
}
private static UserConfig UserConfig => Program.UserConfig;
private static SystemConfig SysConfig => Program.SystemConfig;
private static string Bool(bool value) => value ? "on" : "off";
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 LogRound(int value, int logBase) => (value <= 0 ? 0 : (int)Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
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 SystemName { get; }
private static string SystemEdition { get; }
private static string SystemBuild { get; }
private static int RamSize { get; }
private static string GpuVendor { get; }
private static string[] ProgramArguments { get; }
static AnalyticsReportGenerator(){
string osName, osEdition, osBuild;
try{
using(RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false)){
// ReSharper disable once PossibleNullReferenceException
osName = key.GetValue("ProductName") as string;
osBuild = key.GetValue("CurrentBuild") as string;
osEdition = null;
if (osName != null){
Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");
if (match.Success){
osName = match.Groups[1].Value;
osEdition = match.Groups[2].Value;
}
}
}
}catch{
osName = osEdition = osBuild = null;
}
SystemName = osName ?? "Windows (unknown)";
SystemEdition = osEdition ?? "(unknown)";
SystemBuild = osBuild ?? "(unknown)";
try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory")){
foreach(ManagementBaseObject obj in searcher.Get()){
RamSize += (int)((ulong)obj["Capacity"]/(1024L*1024L));
}
}
}catch{
RamSize = 0;
}
string gpu = null;
try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController")){
foreach(ManagementBaseObject obj in searcher.Get()){
string vendor = obj["Caption"] as string;
if (!string.IsNullOrEmpty(vendor)){
gpu = vendor;
}
}
}
}catch{
// rip
}
GpuVendor = gpu ?? "(unknown)";
Dictionary<string, string> args = new Dictionary<string, string>();
Arguments.GetCurrentClean().ToDictionary(args);
ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
}
private static string TrayMode{
get{
switch(UserConfig.TrayBehavior){
case TrayIcon.Behavior.DisplayOnly: return "icon";
case TrayIcon.Behavior.MinimizeToTray: return "minimize";
case TrayIcon.Behavior.CloseToTray: return "close";
case TrayIcon.Behavior.Combined: return "combined";
default: return "off";
}
}
}
private static string NotificationPosition{
get{
switch(UserConfig.NotificationPosition){
case TweetNotification.Position.TopLeft: return "top left";
case TweetNotification.Position.TopRight: return "top right";
case TweetNotification.Position.BottomLeft: return "bottom left";
case TweetNotification.Position.BottomRight: return "bottom right";
default: return "custom";
}
}
}
private static string NotificationSize{
get{
switch(UserConfig.NotificationSize){
case TweetNotification.Size.Auto: return "auto";
default: return RoundUp(UserConfig.CustomNotificationSize.Width, 20)+"x"+RoundUp(UserConfig.CustomNotificationSize.Height, 20);
}
}
}
private static string NotificationTimer{
get{
if (!UserConfig.DisplayNotificationTimer){
return "off";
}
else{
return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
}
}
}
private static Dictionary<string, string> EditLayoutDesignPluginData{
get{
Dictionary<string, string> dict = new Dictionary<string, string>();
try{
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));
foreach(Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")){
dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
}
}catch{
// rip
}
return dict;
}
}
private static int TemplateCountFromPlugin{
get{
try{
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
}catch{
return 0;
}
}
}
private static string ReplyAccountConfigFromPlugin{
get{
try{
string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");
Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);
if (!matchType.Success){
return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)";
}
string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/"+accType : accType;
}catch{
return "(unknown)";
}
}
}
public class ExternalInfo{
public static ExternalInfo From(Form form){
if (form == null){
return new ExternalInfo();
}
else{
Screen screen = Screen.FromControl(form); // works on multi-monitor setups even in tray
int dpi;
using(Graphics graphics = form.CreateGraphics()){
dpi = (int)graphics.DpiY;
}
return new ExternalInfo{
Resolution = screen.Bounds.Width+"x"+screen.Bounds.Height,
DPI = dpi
};
}
}
public string Resolution { get; private set; }
public int? DPI { get; private set; }
private ExternalInfo(){}
}
}
}

View File

@@ -1,6 +1,5 @@
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
@@ -21,7 +20,7 @@ namespace TweetDuck.Core.Other{
} }
private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ private void OnLinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe(e.Link.LinkData as string); BrowserUtils.OpenExternalBrowser(e.Link.LinkData as string);
} }
private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){ private void FormAbout_HelpRequested(object sender, HelpEventArgs hlpevent){
@@ -34,7 +33,7 @@ namespace TweetDuck.Core.Other{
} }
private void ShowGuide(){ private void ShowGuide(){
new FormGuide().Show(); FormGuide.Show();
Close(); Close();
} }
} }

View File

@@ -6,27 +6,65 @@ 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;
namespace TweetDuck.Core.Other{ namespace TweetDuck.Core.Other{
sealed partial class FormGuide : Form{ sealed partial class FormGuide : Form{
private const string GuideUrl = "https://tweetduck.chylex.com/guide/v1/"; private const string GuideUrl = "https://tweetduck.chylex.com/guide/v2/";
private const string GuidePathRegex = @"^guide(?:/v\d+)?(?:/(#.*))?";
public static bool CheckGuideUrl(string url, out string hash){
if (!url.Contains("//tweetduck.chylex.com/guide")){
hash = null;
return false;
}
string path = url.Substring(url.IndexOf("/guide")+1);
Match match = Regex.Match(path, GuidePathRegex);
if (match.Success){
hash = match.Groups[1].Value;
return true;
}
else{
hash = null;
return false;
}
}
public static void Show(string hash = null){
string url = GuideUrl+(hash ?? string.Empty);
FormGuide guide = FormManager.TryFind<FormGuide>();
if (guide == null){
FormBrowser owner = FormManager.TryFind<FormBrowser>();
if (owner != null){
owner.TriggerAnalyticsEvent(AnalyticsFile.Event.OpenGuide);
new FormGuide(url, owner).Show(owner);
}
}
else{
guide.Reload(url);
guide.Activate();
}
}
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
public FormGuide(){ private FormGuide(string url, Form owner){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Guide"; Text = Program.BrandName+" Guide";
FormBrowser owner = FormManager.TryFind<FormBrowser>();
if (owner != null){ if (owner != null){
Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4); Size = new Size(owner.Size.Width*3/4, owner.Size.Height*3/4);
VisibleChanged += (sender, args) => this.MoveToCenter(owner); VisibleChanged += (sender, args) => this.MoveToCenter(owner);
} }
this.browser = new ChromiumWebBrowser(GuideUrl){ this.browser = new ChromiumWebBrowser(url){
MenuHandler = new ContextMenuGuide(), MenuHandler = new ContextMenuGuide(),
JsDialogHandler = new JavaScriptDialogHandler(), JsDialogHandler = new JavaScriptDialogHandler(),
LifeSpanHandler = new LifeSpanHandler(), LifeSpanHandler = new LifeSpanHandler(),
@@ -49,8 +87,16 @@ namespace TweetDuck.Core.Other{
Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged; Program.UserConfig.ZoomLevelChanged += Config_ZoomLevelChanged;
} }
private void Reload(string url){
browser.LoadingStateChanged += browser_LoadingStateChanged;
browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation;
browser.Load("about:blank");
browser.Load(url);
}
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){ if (!e.IsLoading && browser.Address != "about:blank"){
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
browser.Location = Point.Empty; browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill; browser.Dock = DockStyle.Fill;

View File

@@ -35,8 +35,8 @@ namespace TweetDuck.Core.Other{
} }
private void ReloadPluginList(){ private void ReloadPluginList(){
flowLayoutPlugins.SuspendLayout();
flowLayoutPlugins.Controls.Clear(); flowLayoutPlugins.Controls.Clear();
flowLayoutPlugins.SuspendLayout();
foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){ foreach(Plugin plugin in pluginManager.Plugins.OrderBy(GetPluginOrderIndex).ThenBy(plugin => plugin.Name)){
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin)); flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager, plugin));

View File

@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification.Example;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Settings; using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Other.Settings.Dialogs; using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
@@ -21,7 +24,7 @@ namespace TweetDuck.Core.Other{
public bool ShouldReloadBrowser { get; private set; } public bool ShouldReloadBrowser { get; private set; }
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, Type startTab){ public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates, AnalyticsManager analytics, Type startTab){
InitializeComponent(); InitializeComponent();
Text = Program.BrandName+" Options"; Text = Program.BrandName+" Options";
@@ -33,20 +36,22 @@ namespace TweetDuck.Core.Other{
this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1; this.buttonHeight = BrowserUtils.Scale(39, this.GetDPIScale()) | 1;
AddButton("General", () => new TabSettingsGeneral(updates)); AddButton("General", () => new TabSettingsGeneral(this.browser, updates));
AddButton("Locales", () => new TabSettingsLocales());
AddButton("System Tray", () => new TabSettingsTray()); AddButton("System Tray", () => new TabSettingsTray());
AddButton("Notifications", () => new TabSettingsNotifications(browser.CreateNotificationForm(false))); AddButton("Notifications", () => new TabSettingsNotifications(new FormNotificationExample(this.browser, this.plugins)));
AddButton("Sounds", () => new TabSettingsSounds()); AddButton("Sounds", () => new TabSettingsSounds());
AddButton("Feedback", () => new TabSettingsFeedback()); AddButton("Feedback", () => new TabSettingsFeedback(analytics, AnalyticsReportGenerator.ExternalInfo.From(this.browser), this.plugins));
AddButton("Advanced", () => new TabSettingsAdvanced(browser.ReinjectCustomCSS)); AddButton("Advanced", () => new TabSettingsAdvanced(this.browser.ReinjectCustomCSS));
SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]); SelectTab(tabs[startTab ?? typeof(TabSettingsGeneral)]);
} }
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){ private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
currentTab.Control.OnClosing();
foreach(SettingsTab tab in tabs.Values){ foreach(SettingsTab tab in tabs.Values){
if (tab.IsInitialized){ if (tab.IsInitialized){
tab.Control.OnClosing();
tab.Control.Dispose(); tab.Control.Dispose();
} }
} }
@@ -56,20 +61,21 @@ namespace TweetDuck.Core.Other{
} }
private void btnManageOptions_Click(object sender, EventArgs e){ private void btnManageOptions_Click(object sender, EventArgs e){
foreach(SettingsTab tab in tabs.Values){ currentTab.Control.OnClosing();
if (tab.IsInitialized){
tab.Control.OnClosing();
}
}
using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){ using(DialogSettingsManage dialog = new DialogSettingsManage(plugins)){
FormClosing -= FormSettings_FormClosing;
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){
FormClosing -= FormSettings_FormClosing;
browser.ResumeNotification(); browser.ResumeNotification();
BrowserProcessHandler.UpdatePrefs();
ShouldReloadBrowser = dialog.ShouldReloadBrowser; ShouldReloadBrowser = dialog.ShouldReloadBrowser;
Close(); Close();
} }
else{
FormClosing += FormSettings_FormClosing;
}
} }
} }
@@ -113,6 +119,7 @@ namespace TweetDuck.Core.Other{
private void SelectTab(SettingsTab tab){ private void SelectTab(SettingsTab tab){
if (currentTab != null){ if (currentTab != null){
currentTab.Button.BackColor = SystemColors.Control; currentTab.Button.BackColor = SystemColors.Control;
currentTab.Control.OnClosing();
} }
tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor; tab.Button.BackColor = tab.Button.FlatAppearance.MouseDownBackColor;
@@ -157,11 +164,7 @@ namespace TweetDuck.Core.Other{
private sealed class SettingsTab{ private sealed class SettingsTab{
public Button Button { get; } public Button Button { get; }
public BaseTabSettings Control{ public BaseTabSettings Control => control ?? (control = constructor());
get => control ?? (control = constructor());
set => control = value;
}
public bool IsInitialized => control != null; public bool IsInitialized => control != null;
private readonly Func<BaseTabSettings> constructor; private readonly Func<BaseTabSettings> constructor;

View File

@@ -1,27 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics;
using CefSharp;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Management{
static class BrowserProcesses{
private static readonly Dictionary<int, int> PIDs = new Dictionary<int, int>();
public static void Link(int identifier, int pid){
PIDs[identifier] = pid;
}
public static void Forget(int identifier){
PIDs.Remove(identifier);
}
public static Process FindProcess(IBrowser browser){
if (PIDs.TryGetValue(browser.Identifier, out int pid) && WindowsUtils.IsChildProcess(pid)){ // child process is checked in two places for safety
return Process.GetProcessById(pid);
}
else{
return null;
}
}
}
}

View File

@@ -1,91 +0,0 @@
using System;
using System.Diagnostics;
using System.Timers;
using System.Windows.Forms;
using CefSharp;
using Timer = System.Timers.Timer;
namespace TweetDuck.Core.Other.Management{
sealed class MemoryUsageTracker : IDisposable{
private const int IntervalMemoryCheck = 60000*30; // 30 minutes
private const int IntervalCleanupAttempt = 60000*5; // 5 minutes
private readonly string script;
private readonly Timer timer;
private Form owner;
private IBrowser browser;
private long threshold;
private bool needsCleanup;
public MemoryUsageTracker(string cleanupFunctionName){
this.script = $"window.{cleanupFunctionName} && window.{cleanupFunctionName}()";
this.timer = new Timer{ Interval = IntervalMemoryCheck };
this.timer.Elapsed += timer_Elapsed;
}
public void Start(Form owner, IBrowser browser, int thresholdMB){
Stop();
this.owner = owner;
this.browser = browser;
this.threshold = thresholdMB*1024L*1024L;
this.timer.SynchronizingObject = owner;
this.timer.Start();
}
public void Stop(){
timer.Stop();
timer.SynchronizingObject = null;
owner = null;
browser = null;
SetNeedsCleanup(false);
}
public void Dispose(){
timer.SynchronizingObject = null;
timer.Dispose();
owner = null;
browser = null;
}
private void SetNeedsCleanup(bool value){
if (needsCleanup != value){
needsCleanup = value;
timer.Interval = value ? IntervalCleanupAttempt : IntervalMemoryCheck; // restarts timer
}
}
private void timer_Elapsed(object sender, ElapsedEventArgs e){
if (owner == null || browser == null){
return;
}
if (needsCleanup){
if (!owner.ContainsFocus){
using(IFrame frame = browser.MainFrame){
frame.EvaluateScriptAsync(script).ContinueWith(task => {
JavascriptResponse response = task.Result;
if (response.Success && (response.Result as bool? ?? false)){
SetNeedsCleanup(false);
}
});
}
}
}
else{
try{
using(Process process = BrowserProcesses.FindProcess(browser)){
if (process?.PrivateMemorySize64 > threshold){
SetNeedsCleanup(true);
}
}
}catch{
// ignore I guess?
}
}
}
}
}

View File

@@ -8,8 +8,6 @@ using TweetLib.Communication;
namespace TweetDuck.Core.Other.Management{ namespace TweetDuck.Core.Other.Management{
sealed class VideoPlayer : IDisposable{ sealed class VideoPlayer : IDisposable{
private readonly string PlayerExe = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe");
public bool Running{ public bool Running{
get{ get{
if (currentProcess == null){ if (currentProcess == null){
@@ -25,6 +23,7 @@ namespace TweetDuck.Core.Other.Management{
private readonly Form owner; private readonly Form owner;
private string lastUrl; private string lastUrl;
private string lastUsername;
private Process currentProcess; private Process currentProcess;
private DuplexPipe.Server currentPipe; private DuplexPipe.Server currentPipe;
@@ -35,20 +34,21 @@ namespace TweetDuck.Core.Other.Management{
this.owner.FormClosing += owner_FormClosing; this.owner.FormClosing += owner_FormClosing;
} }
public void Launch(string url){ public void Launch(string url, string username){
if (Running){ if (Running){
Destroy(); Destroy();
isClosing = false; isClosing = false;
} }
lastUrl = url; lastUrl = url;
lastUsername = username;
try{ try{
currentPipe = DuplexPipe.CreateServer(); currentPipe = DuplexPipe.CreateServer();
currentPipe.DataIn += currentPipe_DataIn; currentPipe.DataIn += currentPipe_DataIn;
if ((currentProcess = Process.Start(new ProcessStartInfo{ if ((currentProcess = Process.Start(new ProcessStartInfo{
FileName = PlayerExe, FileName = Path.Combine(Program.ProgramPath, "TweetDuck.Video.exe"),
Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"", Arguments = $"{owner.Handle} {Program.UserConfig.VideoPlayerVolume} \"{url}\" \"{currentPipe.GenerateToken()}\"",
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true RedirectStandardOutput = true
@@ -84,7 +84,7 @@ namespace TweetDuck.Core.Other.Management{
break; break;
case "download": case "download":
TwitterUtils.DownloadVideo(lastUrl); TwitterUtils.DownloadVideo(lastUrl, lastUsername);
break; break;
case "rip": case "rip":
@@ -157,14 +157,14 @@ namespace TweetDuck.Core.Other.Management{
switch(exitCode){ switch(exitCode){
case 3: // CODE_LAUNCH_FAIL case 3: // CODE_LAUNCH_FAIL
if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Error("Video Playback Error", "Error launching video player, this may be caused by missing Windows Media Player. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(lastUrl); BrowserUtils.OpenExternalBrowser(lastUrl);
} }
break; break;
case 4: // CODE_MEDIA_ERROR case 4: // CODE_MEDIA_ERROR
if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in a browser?", FormMessage.Yes, FormMessage.No)){ if (FormMessage.Error("Video Playback Error", "The video could not be loaded, most likely due to unknown format. Do you want to open the video in your browser?", FormMessage.Yes, FormMessage.No)){
BrowserUtils.OpenExternalBrowser(lastUrl); BrowserUtils.OpenExternalBrowser(lastUrl);
} }

View File

@@ -0,0 +1,90 @@
namespace TweetDuck.Core.Other.Settings.Dialogs {
partial class DialogSettingsAnalytics {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.textBoxReport = new System.Windows.Forms.TextBox();
this.btnClose = new System.Windows.Forms.Button();
this.labelInfo = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// textBoxReport
//
this.textBoxReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textBoxReport.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.textBoxReport.Location = new System.Drawing.Point(12, 41);
this.textBoxReport.Multiline = true;
this.textBoxReport.Name = "textBoxReport";
this.textBoxReport.ReadOnly = true;
this.textBoxReport.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxReport.Size = new System.Drawing.Size(460, 480);
this.textBoxReport.TabIndex = 1;
//
// btnClose
//
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.Location = new System.Drawing.Point(416, 527);
this.btnClose.Name = "btnClose";
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnClose.Size = new System.Drawing.Size(56, 23);
this.btnClose.TabIndex = 2;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// labelInfo
//
this.labelInfo.Location = new System.Drawing.Point(12, 9);
this.labelInfo.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.labelInfo.Name = "labelInfo";
this.labelInfo.Size = new System.Drawing.Size(460, 26);
this.labelInfo.TabIndex = 0;
this.labelInfo.Text = "When enabled, this data will be sent over a secure network roughly once every wee" +
"k.\r\nSome numbers in the report were made imprecise on purpose.";
//
// DialogSettingsAnalytics
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(484, 562);
this.Controls.Add(this.labelInfo);
this.Controls.Add(this.btnClose);
this.Controls.Add(this.textBoxReport);
this.MinimumSize = new System.Drawing.Size(450, 340);
this.Name = "DialogSettingsAnalytics";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBoxReport;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Label labelInfo;
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Other.Analytics;
namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsAnalytics : Form{
public string CefArgs => textBoxReport.Text;
public DialogSettingsAnalytics(AnalyticsReport report){
InitializeComponent();
Text = Program.BrandName+" Options - Analytics Report";
textBoxReport.EnableMultilineShortcuts();
textBoxReport.Text = report.ToString().TrimEnd();
textBoxReport.Select(0, 0);
}
private void btnClose_Click(object sender, EventArgs e){
Close();
}
}
}

View File

@@ -35,7 +35,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
private void btnOpenWiki_Click(object sender, EventArgs e){ private void btnOpenWiki_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki"); BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki");
} }
private void btnApply_Click(object sender, EventArgs e){ private void btnApply_Click(object sender, EventArgs e){

View File

@@ -19,7 +19,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
private void btnHelp_Click(object sender, EventArgs e){ private void btnHelp_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("http://peter.sh/experiments/chromium-command-line-switches/"); BrowserUtils.OpenExternalBrowser("http://peter.sh/experiments/chromium-command-line-switches/");
} }
private void btnApply_Click(object sender, EventArgs e){ private void btnApply_Click(object sender, EventArgs e){

View File

@@ -16,7 +16,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
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.Config); cbConfig.Checked = value.HasFlag(ExportFileFlags.UserConfig);
cbSession.Checked = value.HasFlag(ExportFileFlags.Session); cbSession.Checked = value.HasFlag(ExportFileFlags.Session);
cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData); cbPluginData.Checked = value.HasFlag(ExportFileFlags.PluginData);
} }
@@ -42,7 +42,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
private void cbConfig_CheckedChanged(object sender, EventArgs e){ private void cbConfig_CheckedChanged(object sender, EventArgs e){
SetFlag(ExportFileFlags.Config, cbConfig.Checked); SetFlag(ExportFileFlags.UserConfig, cbConfig.Checked);
} }
private void cbSession_CheckedChanged(object sender, EventArgs e){ private void cbSession_CheckedChanged(object sender, EventArgs e){
@@ -63,7 +63,7 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
currentState = State.Reset; currentState = State.Reset;
Text = "Restore Defaults"; Text = "Restore Defaults";
Flags = ExportFileFlags.Config; Flags = ExportFileFlags.UserConfig;
} }
// Import // Import
@@ -108,10 +108,18 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
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.Config)){ if (Flags.HasFlag(ExportFileFlags.UserConfig)){
Program.ResetConfig(); Program.ResetConfig();
} }
if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
try{
File.Delete(Program.SystemConfigFilePath);
}catch(Exception ex){
Program.Reporter.HandleException("System Config Reset Error", "Could not delete system config.", true, ex);
}
}
if (Flags.HasFlag(ExportFileFlags.PluginData)){ if (Flags.HasFlag(ExportFileFlags.PluginData)){
try{ try{
File.Delete(Program.PluginConfigFilePath); File.Delete(Program.PluginConfigFilePath);
@@ -124,6 +132,9 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
if (Flags.HasFlag(ExportFileFlags.Session)){ if (Flags.HasFlag(ExportFileFlags.Session)){
Program.Restart(Arguments.ArgDeleteCookies); Program.Restart(Arguments.ArgDeleteCookies);
} }
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
Program.Restart();
}
else{ else{
ShouldReloadBrowser = true; ShouldReloadBrowser = true;
} }
@@ -139,7 +150,12 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Program.UserConfig.Reload(); Program.UserConfig.Reload();
if (importManager.IsRestarting){ if (importManager.IsRestarting){
Program.Restart(Arguments.ArgImportCookies); if (Flags.HasFlag(ExportFileFlags.Session)){
Program.Restart(Arguments.ArgImportCookies);
}
else if (Flags.HasFlag(ExportFileFlags.SystemConfig)){
Program.Restart();
}
} }
else{ else{
ShouldReloadBrowser = true; ShouldReloadBrowser = true;
@@ -171,6 +187,8 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
} }
Program.UserConfig.Save(); Program.UserConfig.Save();
Program.SystemConfig.Save();
ExportManager manager = new ExportManager(file, plugins); ExportManager manager = new ExportManager(file, plugins);
if (!manager.Export(Flags)){ if (!manager.Export(Flags)){

View File

@@ -29,10 +29,8 @@
this.cbLogging = new System.Windows.Forms.CheckBox(); this.cbLogging = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.cbDebugUpdates = new System.Windows.Forms.CheckBox(); this.cbDebugUpdates = new System.Windows.Forms.CheckBox();
this.comboLocale = new System.Windows.Forms.ComboBox();
this.tbDataFolder = new System.Windows.Forms.TextBox(); this.tbDataFolder = new System.Windows.Forms.TextBox();
this.tbShortcutTarget = new System.Windows.Forms.TextBox(); this.tbShortcutTarget = new System.Windows.Forms.TextBox();
this.labelLocale = new System.Windows.Forms.Label();
this.labelDataFolder = new System.Windows.Forms.Label(); this.labelDataFolder = new System.Windows.Forms.Label();
this.labelShortcutTarget = new System.Windows.Forms.Label(); this.labelShortcutTarget = new System.Windows.Forms.Label();
this.SuspendLayout(); this.SuspendLayout();
@@ -40,7 +38,7 @@
// btnCancel // btnCancel
// //
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(216, 217); this.btnCancel.Location = new System.Drawing.Point(215, 163);
this.btnCancel.Name = "btnCancel"; this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23); this.btnCancel.Size = new System.Drawing.Size(56, 23);
@@ -52,7 +50,7 @@
// btnRestart // btnRestart
// //
this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnRestart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnRestart.Location = new System.Drawing.Point(153, 217); this.btnRestart.Location = new System.Drawing.Point(152, 163);
this.btnRestart.Name = "btnRestart"; this.btnRestart.Name = "btnRestart";
this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.btnRestart.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnRestart.Size = new System.Drawing.Size(57, 23); this.btnRestart.Size = new System.Drawing.Size(57, 23);
@@ -83,54 +81,33 @@
this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases."); this.toolTip.SetToolTip(this.cbDebugUpdates, "Allows updating to pre-releases.");
this.cbDebugUpdates.UseVisualStyleBackColor = true; this.cbDebugUpdates.UseVisualStyleBackColor = true;
// //
// comboLocale
//
this.comboLocale.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboLocale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboLocale.FormattingEnabled = true;
this.comboLocale.Location = new System.Drawing.Point(15, 83);
this.comboLocale.Name = "comboLocale";
this.comboLocale.Size = new System.Drawing.Size(257, 21);
this.comboLocale.TabIndex = 3;
this.toolTip.SetToolTip(this.comboLocale, "Language used for spell checking.");
//
// tbDataFolder // tbDataFolder
// //
this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.tbDataFolder.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.tbDataFolder.Location = new System.Drawing.Point(15, 135); this.tbDataFolder.Location = new System.Drawing.Point(15, 83);
this.tbDataFolder.Name = "tbDataFolder"; this.tbDataFolder.Name = "tbDataFolder";
this.tbDataFolder.Size = new System.Drawing.Size(257, 20); this.tbDataFolder.Size = new System.Drawing.Size(257, 20);
this.tbDataFolder.TabIndex = 5; this.tbDataFolder.TabIndex = 5;
this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder name that will be created in LocalAppData."); this.toolTip.SetToolTip(this.tbDataFolder, "Path to the data folder. Must be either an absolute path,\r\nor a simple folder nam" +
"e that will be created in LocalAppData.");
// //
// tbShortcutTarget // tbShortcutTarget
// //
this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.tbShortcutTarget.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand; this.tbShortcutTarget.Cursor = System.Windows.Forms.Cursors.Hand;
this.tbShortcutTarget.Location = new System.Drawing.Point(15, 186); this.tbShortcutTarget.Location = new System.Drawing.Point(15, 134);
this.tbShortcutTarget.Name = "tbShortcutTarget"; this.tbShortcutTarget.Name = "tbShortcutTarget";
this.tbShortcutTarget.ReadOnly = true; this.tbShortcutTarget.ReadOnly = true;
this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20); this.tbShortcutTarget.Size = new System.Drawing.Size(257, 20);
this.tbShortcutTarget.TabIndex = 7; this.tbShortcutTarget.TabIndex = 7;
this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click); this.tbShortcutTarget.Click += new System.EventHandler(this.tbShortcutTarget_Click);
// //
// labelLocale
//
this.labelLocale.AutoSize = true;
this.labelLocale.Location = new System.Drawing.Point(12, 67);
this.labelLocale.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelLocale.Name = "labelLocale";
this.labelLocale.Size = new System.Drawing.Size(39, 13);
this.labelLocale.TabIndex = 2;
this.labelLocale.Text = "Locale";
//
// labelDataFolder // labelDataFolder
// //
this.labelDataFolder.AutoSize = true; this.labelDataFolder.AutoSize = true;
this.labelDataFolder.Location = new System.Drawing.Point(12, 119); this.labelDataFolder.Location = new System.Drawing.Point(12, 67);
this.labelDataFolder.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelDataFolder.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDataFolder.Name = "labelDataFolder"; this.labelDataFolder.Name = "labelDataFolder";
this.labelDataFolder.Size = new System.Drawing.Size(62, 13); this.labelDataFolder.Size = new System.Drawing.Size(62, 13);
@@ -140,7 +117,7 @@
// labelShortcutTarget // labelShortcutTarget
// //
this.labelShortcutTarget.AutoSize = true; this.labelShortcutTarget.AutoSize = true;
this.labelShortcutTarget.Location = new System.Drawing.Point(12, 170); this.labelShortcutTarget.Location = new System.Drawing.Point(12, 118);
this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelShortcutTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelShortcutTarget.Name = "labelShortcutTarget"; this.labelShortcutTarget.Name = "labelShortcutTarget";
this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13); this.labelShortcutTarget.Size = new System.Drawing.Size(155, 13);
@@ -151,13 +128,11 @@
// //
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(284, 252); this.ClientSize = new System.Drawing.Size(284, 198);
this.Controls.Add(this.tbShortcutTarget); this.Controls.Add(this.tbShortcutTarget);
this.Controls.Add(this.labelShortcutTarget); this.Controls.Add(this.labelShortcutTarget);
this.Controls.Add(this.tbDataFolder); this.Controls.Add(this.tbDataFolder);
this.Controls.Add(this.labelDataFolder); this.Controls.Add(this.labelDataFolder);
this.Controls.Add(this.comboLocale);
this.Controls.Add(this.labelLocale);
this.Controls.Add(this.cbDebugUpdates); this.Controls.Add(this.cbDebugUpdates);
this.Controls.Add(this.cbLogging); this.Controls.Add(this.cbLogging);
this.Controls.Add(this.btnRestart); this.Controls.Add(this.btnRestart);
@@ -180,8 +155,6 @@
private System.Windows.Forms.CheckBox cbLogging; private System.Windows.Forms.CheckBox cbLogging;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.CheckBox cbDebugUpdates; private System.Windows.Forms.CheckBox cbDebugUpdates;
private System.Windows.Forms.Label labelLocale;
private System.Windows.Forms.ComboBox comboLocale;
private System.Windows.Forms.Label labelDataFolder; private System.Windows.Forms.Label labelDataFolder;
private System.Windows.Forms.TextBox tbDataFolder; private System.Windows.Forms.TextBox tbDataFolder;
private System.Windows.Forms.TextBox tbShortcutTarget; private System.Windows.Forms.TextBox tbShortcutTarget;

View File

@@ -1,33 +1,20 @@
using System; using System;
using System.IO;
using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration; using TweetDuck.Configuration;
using TweetDuck.Data; using TweetDuck.Data;
namespace TweetDuck.Core.Other.Settings.Dialogs{ namespace TweetDuck.Core.Other.Settings.Dialogs{
sealed partial class DialogSettingsRestart : Form{ sealed partial class DialogSettingsRestart : Form{
private const string DefaultLocale = "en-US";
public CommandLineArgs Args { get; private set; } public CommandLineArgs Args { get; private set; }
public DialogSettingsRestart(CommandLineArgs currentArgs){ public DialogSettingsRestart(CommandLineArgs currentArgs){
InitializeComponent(); InitializeComponent();
try{
object[] locales = Directory.EnumerateFiles(Path.Combine(Program.ProgramPath, "locales"), "*.pak", SearchOption.TopDirectoryOnly).Select(Path.GetFileNameWithoutExtension).ToArray<object>();
comboLocale.Items.AddRange(locales);
}catch{
comboLocale.Items.Add(DefaultLocale);
}
cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging); cbLogging.Checked = currentArgs.HasFlag(Arguments.ArgLogging);
cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates); cbDebugUpdates.Checked = currentArgs.HasFlag(Arguments.ArgDebugUpdates);
comboLocale.SelectedItem = currentArgs.GetValue(Arguments.ArgLocale, DefaultLocale);
cbLogging.CheckedChanged += control_Change; cbLogging.CheckedChanged += control_Change;
cbDebugUpdates.CheckedChanged += control_Change; cbDebugUpdates.CheckedChanged += control_Change;
comboLocale.SelectedValueChanged += control_Change;
if (Program.IsPortable){ if (Program.IsPortable){
tbDataFolder.Text = "Not available in portable version"; tbDataFolder.Text = "Not available in portable version";
@@ -54,12 +41,6 @@ namespace TweetDuck.Core.Other.Settings.Dialogs{
Args.AddFlag(Arguments.ArgDebugUpdates); Args.AddFlag(Arguments.ArgDebugUpdates);
} }
string locale = comboLocale.SelectedItem as string;
if (!string.IsNullOrWhiteSpace(locale) && locale != DefaultLocale){
Args.SetValue(Arguments.ArgLocale, locale);
}
if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){ if (!string.IsNullOrWhiteSpace(tbDataFolder.Text) && tbDataFolder.Enabled){
Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text); Args.SetValue(Arguments.ArgDataFolder, tbDataFolder.Text);
} }

View File

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

View File

@@ -25,10 +25,14 @@ namespace TweetDuck.Core.Other.Settings.Export{
public bool Export(ExportFileFlags flags){ public bool Export(ExportFileFlags flags){
try{ try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
if (flags.HasFlag(ExportFileFlags.Config)){ if (flags.HasFlag(ExportFileFlags.UserConfig)){
stream.WriteFile("config", Program.UserConfigFilePath); stream.WriteFile("config", Program.UserConfigFilePath);
} }
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
stream.WriteFile("system", Program.SystemConfigFilePath);
}
if (flags.HasFlag(ExportFileFlags.PluginData)){ if (flags.HasFlag(ExportFileFlags.PluginData)){
stream.WriteFile("plugin.config", Program.PluginConfigFilePath); stream.WriteFile("plugin.config", Program.PluginConfigFilePath);
@@ -67,7 +71,11 @@ 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.Config; flags |= ExportFileFlags.UserConfig;
break;
case "system":
flags |= ExportFileFlags.SystemConfig;
break; break;
case "plugin.config": case "plugin.config":
@@ -99,12 +107,20 @@ 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.Config)){ if (flags.HasFlag(ExportFileFlags.UserConfig)){
entry.WriteToFile(Program.UserConfigFilePath); entry.WriteToFile(Program.UserConfigFilePath);
} }
break; break;
case "system":
if (flags.HasFlag(ExportFileFlags.SystemConfig)){
entry.WriteToFile(Program.SystemConfigFilePath);
IsRestarting = true;
}
break;
case "plugin.config": case "plugin.config":
if (flags.HasFlag(ExportFileFlags.PluginData)){ if (flags.HasFlag(ExportFileFlags.PluginData)){
entry.WriteToFile(Program.PluginConfigFilePath); entry.WriteToFile(Program.PluginConfigFilePath);

View File

@@ -33,42 +33,42 @@
this.btnRestart = new System.Windows.Forms.Button(); this.btnRestart = new System.Windows.Forms.Button();
this.btnOpenAppFolder = new System.Windows.Forms.Button(); this.btnOpenAppFolder = new System.Windows.Forms.Button();
this.btnOpenDataFolder = new System.Windows.Forms.Button(); this.btnOpenDataFolder = new System.Windows.Forms.Button();
this.numMemoryThreshold = new TweetDuck.Core.Controls.NumericUpDownEx(); this.numClearCacheThreshold = new TweetDuck.Core.Controls.NumericUpDownEx();
this.checkBrowserGCReload = new System.Windows.Forms.CheckBox(); this.checkClearCacheAuto = new System.Windows.Forms.CheckBox();
this.labelApp = new System.Windows.Forms.Label(); this.labelApp = new System.Windows.Forms.Label();
this.panelApp = new System.Windows.Forms.Panel(); this.panelAppButtons = new System.Windows.Forms.Panel();
this.labelPerformance = new System.Windows.Forms.Label(); this.labelPerformance = new System.Windows.Forms.Label();
this.panelPerformance = new System.Windows.Forms.Panel(); this.panelClearCacheAuto = new System.Windows.Forms.Panel();
this.labelMemoryUsage = new System.Windows.Forms.Label(); this.labelCache = new System.Windows.Forms.Label();
this.panelConfiguration = new System.Windows.Forms.Panel(); this.panelConfiguration = new System.Windows.Forms.Panel();
this.labelConfiguration = new System.Windows.Forms.Label(); this.labelConfiguration = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).BeginInit(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.panelApp.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).BeginInit();
this.panelPerformance.SuspendLayout(); this.panelAppButtons.SuspendLayout();
this.panelClearCacheAuto.SuspendLayout();
this.panelConfiguration.SuspendLayout(); this.panelConfiguration.SuspendLayout();
this.flowPanel.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// btnClearCache // btnClearCache
// //
this.btnClearCache.Location = new System.Drawing.Point(5, 28); this.btnClearCache.Location = new System.Drawing.Point(5, 172);
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 = 1;
this.btnClearCache.Text = "Clear Cache (calculating)"; this.btnClearCache.Text = "Clear Cache (calculating)";
this.toolTip.SetToolTip(this.btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
this.btnClearCache.UseVisualStyleBackColor = true; this.btnClearCache.UseVisualStyleBackColor = true;
// //
// checkHardwareAcceleration // checkHardwareAcceleration
// //
this.checkHardwareAcceleration.AutoSize = true; this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 5); this.checkHardwareAcceleration.Location = new System.Drawing.Point(6, 124);
this.checkHardwareAcceleration.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 0;
this.checkHardwareAcceleration.Text = "Hardware Acceleration"; this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.toolTip.SetToolTip(this.checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
this.checkHardwareAcceleration.UseVisualStyleBackColor = true; this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
// //
// btnEditCefArgs // btnEditCefArgs
@@ -79,7 +79,6 @@
this.btnEditCefArgs.Size = new System.Drawing.Size(144, 23); this.btnEditCefArgs.Size = new System.Drawing.Size(144, 23);
this.btnEditCefArgs.TabIndex = 0; this.btnEditCefArgs.TabIndex = 0;
this.btnEditCefArgs.Text = "Edit CEF Arguments"; this.btnEditCefArgs.Text = "Edit CEF Arguments";
this.toolTip.SetToolTip(this.btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
this.btnEditCefArgs.UseVisualStyleBackColor = true; this.btnEditCefArgs.UseVisualStyleBackColor = true;
// //
// btnEditCSS // btnEditCSS
@@ -89,7 +88,6 @@
this.btnEditCSS.Size = new System.Drawing.Size(144, 23); this.btnEditCSS.Size = new System.Drawing.Size(144, 23);
this.btnEditCSS.TabIndex = 1; this.btnEditCSS.TabIndex = 1;
this.btnEditCSS.Text = "Edit CSS"; this.btnEditCSS.Text = "Edit CSS";
this.toolTip.SetToolTip(this.btnEditCSS, "Set custom CSS for browser and notification windows.");
this.btnEditCSS.UseVisualStyleBackColor = true; this.btnEditCSS.UseVisualStyleBackColor = true;
// //
// btnRestartArgs // btnRestartArgs
@@ -99,7 +97,6 @@
this.btnRestartArgs.Size = new System.Drawing.Size(144, 23); this.btnRestartArgs.Size = new System.Drawing.Size(144, 23);
this.btnRestartArgs.TabIndex = 3; this.btnRestartArgs.TabIndex = 3;
this.btnRestartArgs.Text = "Restart with Arguments"; this.btnRestartArgs.Text = "Restart with Arguments";
this.toolTip.SetToolTip(this.btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments.");
this.btnRestartArgs.UseVisualStyleBackColor = true; this.btnRestartArgs.UseVisualStyleBackColor = true;
// //
// btnRestart // btnRestart
@@ -109,7 +106,6 @@
this.btnRestart.Size = new System.Drawing.Size(144, 23); this.btnRestart.Size = new System.Drawing.Size(144, 23);
this.btnRestart.TabIndex = 2; this.btnRestart.TabIndex = 2;
this.btnRestart.Text = "Restart the Program"; this.btnRestart.Text = "Restart the Program";
this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
this.btnRestart.UseVisualStyleBackColor = true; this.btnRestart.UseVisualStyleBackColor = true;
// //
// btnOpenAppFolder // btnOpenAppFolder
@@ -120,7 +116,6 @@
this.btnOpenAppFolder.Size = new System.Drawing.Size(144, 23); this.btnOpenAppFolder.Size = new System.Drawing.Size(144, 23);
this.btnOpenAppFolder.TabIndex = 0; this.btnOpenAppFolder.TabIndex = 0;
this.btnOpenAppFolder.Text = "Open Program Folder"; this.btnOpenAppFolder.Text = "Open Program Folder";
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
this.btnOpenAppFolder.UseVisualStyleBackColor = true; this.btnOpenAppFolder.UseVisualStyleBackColor = true;
// //
// btnOpenDataFolder // btnOpenDataFolder
@@ -131,104 +126,94 @@
this.btnOpenDataFolder.Size = new System.Drawing.Size(144, 23); this.btnOpenDataFolder.Size = new System.Drawing.Size(144, 23);
this.btnOpenDataFolder.TabIndex = 1; this.btnOpenDataFolder.TabIndex = 1;
this.btnOpenDataFolder.Text = "Open Data Folder"; this.btnOpenDataFolder.Text = "Open Data Folder";
this.toolTip.SetToolTip(this.btnOpenDataFolder, "Opens the folder where your profile data is located.");
this.btnOpenDataFolder.UseVisualStyleBackColor = true; this.btnOpenDataFolder.UseVisualStyleBackColor = true;
// //
// numMemoryThreshold // numClearCacheThreshold
// //
this.numMemoryThreshold.Increment = new decimal(new int[] { this.numClearCacheThreshold.Increment = 50;
50, this.numClearCacheThreshold.Location = new System.Drawing.Point(227, 4);
0, this.numClearCacheThreshold.Maximum = 1000;
0, this.numClearCacheThreshold.Minimum = 100;
0}); this.numClearCacheThreshold.Name = "numClearCacheThreshold";
this.numMemoryThreshold.Location = new System.Drawing.Point(202, 82); this.numClearCacheThreshold.Size = new System.Drawing.Size(72, 20);
this.numMemoryThreshold.Maximum = 2000; this.numClearCacheThreshold.TabIndex = 4;
this.numMemoryThreshold.Minimum = 200; this.numClearCacheThreshold.TextSuffix = " MB";
this.numMemoryThreshold.Name = "numMemoryThreshold"; this.numClearCacheThreshold.Value = 250;
this.numMemoryThreshold.Size = new System.Drawing.Size(97, 20);
this.numMemoryThreshold.TabIndex = 4;
this.numMemoryThreshold.TextSuffix = " MB";
this.toolTip.SetToolTip(this.numMemoryThreshold, "Minimum amount of memory usage by the browser process to trigger the cleanup.\r\nThis is not a limit, the usage is allowed to exceed this value.");
this.numMemoryThreshold.Value = 400;
// //
// checkBrowserGCReload // checkClearCacheAuto
// //
this.checkBrowserGCReload.AutoSize = true; this.checkClearCacheAuto.AutoSize = true;
this.checkBrowserGCReload.Location = new System.Drawing.Point(6, 84); this.checkClearCacheAuto.Location = new System.Drawing.Point(6, 6);
this.checkBrowserGCReload.Margin = new System.Windows.Forms.Padding(6, 5, 3, 3); this.checkClearCacheAuto.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkBrowserGCReload.Name = "checkBrowserGCReload"; this.checkClearCacheAuto.Name = "checkClearCacheAuto";
this.checkBrowserGCReload.Size = new System.Drawing.Size(190, 17); this.checkClearCacheAuto.Size = new System.Drawing.Size(215, 17);
this.checkBrowserGCReload.TabIndex = 3; this.checkClearCacheAuto.TabIndex = 3;
this.checkBrowserGCReload.Text = "Enable Browser Memory Threshold"; this.checkClearCacheAuto.Text = "Clear Cache Automatically When Above";
this.toolTip.SetToolTip(this.checkBrowserGCReload, "Automatically reloads TweetDeck to save memory. This option only works if\r\nthe browser is in a \'default state\', i.e. all modals and drawers are closed, and\r\nall columns are scrolled to top. This option will not be exported in a profile."); this.checkClearCacheAuto.UseVisualStyleBackColor = true;
this.checkBrowserGCReload.UseVisualStyleBackColor = true;
// //
// labelApp // labelApp
// //
this.labelApp.AutoSize = true; this.labelApp.AutoSize = true;
this.labelApp.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelApp.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelApp.Location = new System.Drawing.Point(6, 8); this.labelApp.Location = new System.Drawing.Point(0, 0);
this.labelApp.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.labelApp.Margin = new System.Windows.Forms.Padding(0);
this.labelApp.Name = "labelApp"; this.labelApp.Name = "labelApp";
this.labelApp.Size = new System.Drawing.Size(38, 20); this.labelApp.Size = new System.Drawing.Size(38, 20);
this.labelApp.TabIndex = 0; this.labelApp.TabIndex = 0;
this.labelApp.Text = "App"; this.labelApp.Text = "App";
// //
// panelApp // panelAppButtons
// //
this.panelApp.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelAppButtons.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right))); this.panelAppButtons.Controls.Add(this.btnOpenDataFolder);
this.panelApp.Controls.Add(this.btnOpenDataFolder); this.panelAppButtons.Controls.Add(this.btnOpenAppFolder);
this.panelApp.Controls.Add(this.btnOpenAppFolder); this.panelAppButtons.Controls.Add(this.btnRestart);
this.panelApp.Controls.Add(this.btnRestart); this.panelAppButtons.Controls.Add(this.btnRestartArgs);
this.panelApp.Controls.Add(this.btnRestartArgs); this.panelAppButtons.Location = new System.Drawing.Point(0, 20);
this.panelApp.Location = new System.Drawing.Point(9, 31); this.panelAppButtons.Margin = new System.Windows.Forms.Padding(0);
this.panelApp.Name = "panelApp"; this.panelAppButtons.Name = "panelAppButtons";
this.panelApp.Size = new System.Drawing.Size(322, 59); this.panelAppButtons.Size = new System.Drawing.Size(322, 58);
this.panelApp.TabIndex = 1; this.panelAppButtons.TabIndex = 1;
// //
// labelPerformance // labelPerformance
// //
this.labelPerformance.AutoSize = true; this.labelPerformance.AutoSize = true;
this.labelPerformance.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelPerformance.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelPerformance.Location = new System.Drawing.Point(6, 114); this.labelPerformance.Location = new System.Drawing.Point(0, 98);
this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 21, 0, 0); this.labelPerformance.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelPerformance.Name = "labelPerformance"; this.labelPerformance.Name = "labelPerformance";
this.labelPerformance.Size = new System.Drawing.Size(100, 20); this.labelPerformance.Size = new System.Drawing.Size(100, 20);
this.labelPerformance.TabIndex = 2; this.labelPerformance.TabIndex = 2;
this.labelPerformance.Text = "Performance"; this.labelPerformance.Text = "Performance";
// //
// panelPerformance // panelClearCacheAuto
// //
this.panelPerformance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelClearCacheAuto.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right))); this.panelClearCacheAuto.Controls.Add(this.checkClearCacheAuto);
this.panelPerformance.Controls.Add(this.checkBrowserGCReload); this.panelClearCacheAuto.Controls.Add(this.numClearCacheThreshold);
this.panelPerformance.Controls.Add(this.numMemoryThreshold); this.panelClearCacheAuto.Location = new System.Drawing.Point(0, 198);
this.panelPerformance.Controls.Add(this.labelMemoryUsage); this.panelClearCacheAuto.Margin = new System.Windows.Forms.Padding(0);
this.panelPerformance.Controls.Add(this.checkHardwareAcceleration); this.panelClearCacheAuto.Name = "panelClearCacheAuto";
this.panelPerformance.Controls.Add(this.btnClearCache); this.panelClearCacheAuto.Size = new System.Drawing.Size(322, 26);
this.panelPerformance.Location = new System.Drawing.Point(9, 137); this.panelClearCacheAuto.TabIndex = 3;
this.panelPerformance.Name = "panelPerformance";
this.panelPerformance.Size = new System.Drawing.Size(322, 105);
this.panelPerformance.TabIndex = 3;
// //
// labelMemoryUsage // labelCache
// //
this.labelMemoryUsage.AutoSize = true; this.labelCache.AutoSize = true;
this.labelMemoryUsage.Location = new System.Drawing.Point(3, 66); this.labelCache.Location = new System.Drawing.Point(3, 156);
this.labelMemoryUsage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0); this.labelCache.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelMemoryUsage.Name = "labelMemoryUsage"; this.labelCache.Name = "labelCache";
this.labelMemoryUsage.Size = new System.Drawing.Size(78, 13); this.labelCache.Size = new System.Drawing.Size(38, 13);
this.labelMemoryUsage.TabIndex = 2; this.labelCache.TabIndex = 2;
this.labelMemoryUsage.Text = "Memory Usage"; this.labelCache.Text = "Cache";
// //
// panelConfiguration // panelConfiguration
// //
this.panelConfiguration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelConfiguration.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right)));
this.panelConfiguration.Controls.Add(this.btnEditCSS); this.panelConfiguration.Controls.Add(this.btnEditCSS);
this.panelConfiguration.Controls.Add(this.btnEditCefArgs); this.panelConfiguration.Controls.Add(this.btnEditCefArgs);
this.panelConfiguration.Location = new System.Drawing.Point(9, 289); this.panelConfiguration.Location = new System.Drawing.Point(0, 264);
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 = 5;
@@ -237,32 +222,49 @@
// //
this.labelConfiguration.AutoSize = true; this.labelConfiguration.AutoSize = true;
this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelConfiguration.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelConfiguration.Location = new System.Drawing.Point(6, 266); this.labelConfiguration.Location = new System.Drawing.Point(0, 244);
this.labelConfiguration.Margin = new System.Windows.Forms.Padding(0, 21, 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 = 4;
this.labelConfiguration.Text = "Configuration"; this.labelConfiguration.Text = "Configuration";
// //
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Controls.Add(this.labelApp);
this.flowPanel.Controls.Add(this.panelAppButtons);
this.flowPanel.Controls.Add(this.labelPerformance);
this.flowPanel.Controls.Add(this.checkHardwareAcceleration);
this.flowPanel.Controls.Add(this.labelCache);
this.flowPanel.Controls.Add(this.btnClearCache);
this.flowPanel.Controls.Add(this.panelClearCacheAuto);
this.flowPanel.Controls.Add(this.labelConfiguration);
this.flowPanel.Controls.Add(this.panelConfiguration);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 295);
this.flowPanel.TabIndex = 6;
this.flowPanel.WrapContents = false;
//
// TabSettingsAdvanced // TabSettingsAdvanced
// //
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.labelConfiguration); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.panelConfiguration);
this.Controls.Add(this.panelPerformance);
this.Controls.Add(this.labelPerformance);
this.Controls.Add(this.panelApp);
this.Controls.Add(this.labelApp);
this.Name = "TabSettingsAdvanced"; this.Name = "TabSettingsAdvanced";
this.Size = new System.Drawing.Size(340, 328); this.Size = new System.Drawing.Size(340, 313);
((System.ComponentModel.ISupportInitialize)(this.numMemoryThreshold)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numClearCacheThreshold)).EndInit();
this.panelApp.ResumeLayout(false); this.panelAppButtons.ResumeLayout(false);
this.panelPerformance.ResumeLayout(false); this.panelClearCacheAuto.ResumeLayout(false);
this.panelPerformance.PerformLayout(); this.panelClearCacheAuto.PerformLayout();
this.panelConfiguration.ResumeLayout(false); this.panelConfiguration.ResumeLayout(false);
this.flowPanel.ResumeLayout(false);
this.flowPanel.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
@@ -278,13 +280,14 @@
private System.Windows.Forms.Button btnOpenAppFolder; private System.Windows.Forms.Button btnOpenAppFolder;
private System.Windows.Forms.Button btnOpenDataFolder; private System.Windows.Forms.Button btnOpenDataFolder;
private System.Windows.Forms.Label labelApp; private System.Windows.Forms.Label labelApp;
private System.Windows.Forms.Panel panelApp; private System.Windows.Forms.Panel panelAppButtons;
private System.Windows.Forms.Label labelPerformance; private System.Windows.Forms.Label labelPerformance;
private System.Windows.Forms.Panel panelPerformance; private System.Windows.Forms.Panel panelClearCacheAuto;
private System.Windows.Forms.Panel panelConfiguration; private System.Windows.Forms.Panel panelConfiguration;
private System.Windows.Forms.Label labelConfiguration; private System.Windows.Forms.Label labelConfiguration;
private System.Windows.Forms.Label labelMemoryUsage; private System.Windows.Forms.Label labelCache;
private Controls.NumericUpDownEx numMemoryThreshold; private Controls.NumericUpDownEx numClearCacheThreshold;
private System.Windows.Forms.CheckBox checkBrowserGCReload; private System.Windows.Forms.CheckBox checkClearCacheAuto;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
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;
@@ -17,6 +18,19 @@ namespace TweetDuck.Core.Other.Settings{
this.reinjectBrowserCSS = reinjectBrowserCSS; this.reinjectBrowserCSS = reinjectBrowserCSS;
toolTip.SetToolTip(btnOpenAppFolder, "Opens the folder where the app is located.");
toolTip.SetToolTip(btnOpenDataFolder, "Opens the folder where your profile data is located.");
toolTip.SetToolTip(btnRestart, "Restarts the program using the same command\r\nline arguments that were used at launch.");
toolTip.SetToolTip(btnRestartArgs, "Restarts the program with customizable\r\ncommand line arguments.");
toolTip.SetToolTip(checkHardwareAcceleration, "Uses graphics card to improve performance. Disable if you experience\r\nvisual glitches. This option will not be exported in a profile.");
toolTip.SetToolTip(btnClearCache, "Clearing cache will free up space taken by downloaded images and other resources.");
toolTip.SetToolTip(checkClearCacheAuto, "Automatically clears cache when its size exceeds the set threshold. Note that cache can only be cleared when closing TweetDuck.");
toolTip.SetToolTip(btnEditCefArgs, "Set custom command line arguments for Chromium Embedded Framework.");
toolTip.SetToolTip(btnEditCSS, "Set custom CSS for browser and notification windows.");
if (SystemConfig.IsHardwareAccelerationSupported){ if (SystemConfig.IsHardwareAccelerationSupported){
checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration; checkHardwareAcceleration.Checked = SysConfig.HardwareAcceleration;
} }
@@ -25,37 +39,34 @@ namespace TweetDuck.Core.Other.Settings{
checkHardwareAcceleration.Checked = false; checkHardwareAcceleration.Checked = false;
} }
checkBrowserGCReload.Checked = SysConfig.EnableBrowserGCReload; checkClearCacheAuto.Checked = SysConfig.ClearCacheAutomatically;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked; numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked;
numMemoryThreshold.SetValueSafe(SysConfig.BrowserMemoryThreshold); numClearCacheThreshold.SetValueSafe(SysConfig.ClearCacheThreshold);
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => { BrowserCache.GetCacheSize(task => {
if (bytes == -1L){ string text = task.Status == TaskStatus.RanToCompletion ? (int)Math.Ceiling(task.Result/(1024.0*1024.0))+" MB" : "unknown";
btnClearCache.Text = "Clear Cache (unknown size)"; this.InvokeSafe(() => btnClearCache.Text = $"Clear Cache ({text})");
} });
else{
btnClearCache.Text = "Clear Cache ("+(int)Math.Ceiling(bytes/(1024.0*1024.0))+" MB)";
}
}));
} }
public override void OnReady(){ public override void OnReady(){
btnClearCache.Click += btnClearCache_Click;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
checkBrowserGCReload.CheckedChanged += checkBrowserGCReload_CheckedChanged;
numMemoryThreshold.ValueChanged += numMemoryThreshold_ValueChanged;
btnEditCefArgs.Click += btnEditCefArgs_Click;
btnEditCSS.Click += btnEditCSS_Click;
btnOpenAppFolder.Click += btnOpenAppFolder_Click; btnOpenAppFolder.Click += btnOpenAppFolder_Click;
btnOpenDataFolder.Click += btnOpenDataFolder_Click; btnOpenDataFolder.Click += btnOpenDataFolder_Click;
btnRestart.Click += btnRestart_Click; btnRestart.Click += btnRestart_Click;
btnRestartArgs.Click += btnRestartArgs_Click; btnRestartArgs.Click += btnRestartArgs_Click;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
btnClearCache.Click += btnClearCache_Click;
checkClearCacheAuto.CheckedChanged += checkClearCacheAuto_CheckedChanged;
btnEditCefArgs.Click += btnEditCefArgs_Click;
btnEditCSS.Click += btnEditCSS_Click;
} }
public override void OnClosing(){ public override void OnClosing(){
SysConfig.ClearCacheAutomatically = checkClearCacheAuto.Checked;
SysConfig.ClearCacheThreshold = (int)numClearCacheThreshold.Value;
SysConfig.Save(); SysConfig.Save();
} }
@@ -65,20 +76,15 @@ namespace TweetDuck.Core.Other.Settings{
FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK); FormMessage.Information("Clear Cache", "Cache will be automatically cleared when TweetDuck exits.", FormMessage.OK);
} }
private void checkClearCacheAuto_CheckedChanged(object sender, EventArgs e){
numClearCacheThreshold.Enabled = checkClearCacheAuto.Checked;
}
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){ private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked; SysConfig.HardwareAcceleration = checkHardwareAcceleration.Checked;
PromptRestart(); // calls OnClosing PromptRestart(); // calls OnClosing
} }
private void checkBrowserGCReload_CheckedChanged(object sender, EventArgs e){
SysConfig.EnableBrowserGCReload = checkBrowserGCReload.Checked;
numMemoryThreshold.Enabled = checkBrowserGCReload.Checked;
}
private void numMemoryThreshold_ValueChanged(object sender, EventArgs e){
SysConfig.BrowserMemoryThreshold = (int)numMemoryThreshold.Value;
}
private void btnEditCefArgs_Click(object sender, EventArgs e){ private void btnEditCefArgs_Click(object sender, EventArgs e){
DialogSettingsCefArgs form = new DialogSettingsCefArgs(); DialogSettingsCefArgs form = new DialogSettingsCefArgs();

View File

@@ -24,35 +24,37 @@
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.panelFeedback = new System.Windows.Forms.Panel(); this.panelDataCollection = new System.Windows.Forms.Panel();
this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel(); this.labelDataCollectionLink = new System.Windows.Forms.LinkLabel();
this.checkDataCollection = new System.Windows.Forms.CheckBox(); this.checkDataCollection = new System.Windows.Forms.CheckBox();
this.labelDataCollectionMessage = new System.Windows.Forms.Label();
this.btnViewReport = new System.Windows.Forms.Button();
this.btnSendFeedback = new System.Windows.Forms.Button();
this.labelDataCollection = new System.Windows.Forms.Label(); this.labelDataCollection = new System.Windows.Forms.Label();
this.labelFeedback = new System.Windows.Forms.Label(); this.labelFeedback = new System.Windows.Forms.Label();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.btnSendFeedback = new System.Windows.Forms.Button(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.panelFeedback.SuspendLayout(); this.panelDataCollection.SuspendLayout();
this.flowPanel.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// panelFeedback // panelDataCollection
// //
this.panelFeedback.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelDataCollection.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right))); this.panelDataCollection.Controls.Add(this.labelDataCollectionLink);
this.panelFeedback.Controls.Add(this.btnSendFeedback); this.panelDataCollection.Controls.Add(this.checkDataCollection);
this.panelFeedback.Controls.Add(this.labelDataCollectionLink); this.panelDataCollection.Location = new System.Drawing.Point(0, 74);
this.panelFeedback.Controls.Add(this.checkDataCollection); this.panelDataCollection.Margin = new System.Windows.Forms.Padding(0);
this.panelFeedback.Controls.Add(this.labelDataCollection); this.panelDataCollection.Name = "panelDataCollection";
this.panelFeedback.Location = new System.Drawing.Point(9, 31); this.panelDataCollection.Size = new System.Drawing.Size(322, 26);
this.panelFeedback.Name = "panelFeedback"; this.panelDataCollection.TabIndex = 1;
this.panelFeedback.Size = new System.Drawing.Size(322, 80);
this.panelFeedback.TabIndex = 1;
// //
// labelDataCollectionLink // labelDataCollectionLink
// //
this.labelDataCollectionLink.AutoSize = true; this.labelDataCollectionLink.AutoSize = true;
this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10); this.labelDataCollectionLink.LinkArea = new System.Windows.Forms.LinkArea(1, 10);
this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline; this.labelDataCollectionLink.LinkBehavior = System.Windows.Forms.LinkBehavior.HoverUnderline;
this.labelDataCollectionLink.Location = new System.Drawing.Point(141, 60); this.labelDataCollectionLink.Location = new System.Drawing.Point(141, 6);
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);
@@ -60,23 +62,53 @@
this.labelDataCollectionLink.TabStop = true; this.labelDataCollectionLink.TabStop = true;
this.labelDataCollectionLink.Text = "(learn more)"; this.labelDataCollectionLink.Text = "(learn more)";
this.labelDataCollectionLink.UseCompatibleTextRendering = true; this.labelDataCollectionLink.UseCompatibleTextRendering = true;
this.labelDataCollectionLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.labelDataCollectionLink_LinkClicked);
// //
// checkDataCollection // checkDataCollection
// //
this.checkDataCollection.AutoSize = true; this.checkDataCollection.AutoSize = true;
this.checkDataCollection.Location = new System.Drawing.Point(6, 59); this.checkDataCollection.Location = new System.Drawing.Point(6, 6);
this.checkDataCollection.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 2;
this.checkDataCollection.Text = "Send Anonymous Data"; this.checkDataCollection.Text = "Send Anonymous Data";
this.checkDataCollection.UseVisualStyleBackColor = true; this.checkDataCollection.UseVisualStyleBackColor = true;
// //
// labelDataCollectionMessage
//
this.labelDataCollectionMessage.Location = new System.Drawing.Point(6, 135);
this.labelDataCollectionMessage.Margin = new System.Windows.Forms.Padding(6);
this.labelDataCollectionMessage.Name = "labelDataCollectionMessage";
this.labelDataCollectionMessage.Size = new System.Drawing.Size(310, 67);
this.labelDataCollectionMessage.TabIndex = 5;
//
// btnViewReport
//
this.btnViewReport.AutoSize = true;
this.btnViewReport.Location = new System.Drawing.Point(5, 103);
this.btnViewReport.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnViewReport.Name = "btnViewReport";
this.btnViewReport.Size = new System.Drawing.Size(144, 23);
this.btnViewReport.TabIndex = 4;
this.btnViewReport.Text = "View My Analytics Report";
this.btnViewReport.UseVisualStyleBackColor = true;
//
// btnSendFeedback
//
this.btnSendFeedback.AutoSize = true;
this.btnSendFeedback.Location = new System.Drawing.Point(5, 23);
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.btnSendFeedback.Name = "btnSendFeedback";
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23);
this.btnSendFeedback.TabIndex = 0;
this.btnSendFeedback.Text = "Send Feedback / Bug Report";
this.btnSendFeedback.UseVisualStyleBackColor = true;
//
// labelDataCollection // labelDataCollection
// //
this.labelDataCollection.AutoSize = true; this.labelDataCollection.AutoSize = true;
this.labelDataCollection.Location = new System.Drawing.Point(3, 41); this.labelDataCollection.Location = new System.Drawing.Point(3, 61);
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);
@@ -87,48 +119,57 @@
// //
this.labelFeedback.AutoSize = true; this.labelFeedback.AutoSize = true;
this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelFeedback.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelFeedback.Location = new System.Drawing.Point(6, 8); this.labelFeedback.Location = new System.Drawing.Point(0, 0);
this.labelFeedback.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.labelFeedback.Margin = new System.Windows.Forms.Padding(0);
this.labelFeedback.Name = "labelFeedback"; this.labelFeedback.Name = "labelFeedback";
this.labelFeedback.Size = new System.Drawing.Size(80, 20); this.labelFeedback.Size = new System.Drawing.Size(80, 20);
this.labelFeedback.TabIndex = 0; this.labelFeedback.TabIndex = 0;
this.labelFeedback.Text = "Feedback"; this.labelFeedback.Text = "Feedback";
// //
// btnSendFeedback // flowPanel
// //
this.btnSendFeedback.AutoSize = true; this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.btnSendFeedback.Location = new System.Drawing.Point(5, 3); | System.Windows.Forms.AnchorStyles.Left)
this.btnSendFeedback.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3); | System.Windows.Forms.AnchorStyles.Right)));
this.btnSendFeedback.Name = "btnSendFeedback"; this.flowPanel.Controls.Add(this.labelFeedback);
this.btnSendFeedback.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0); this.flowPanel.Controls.Add(this.btnSendFeedback);
this.btnSendFeedback.Size = new System.Drawing.Size(164, 23); this.flowPanel.Controls.Add(this.labelDataCollection);
this.btnSendFeedback.TabIndex = 0; this.flowPanel.Controls.Add(this.panelDataCollection);
this.btnSendFeedback.Text = "Send Feedback / Bug Report"; this.flowPanel.Controls.Add(this.btnViewReport);
this.btnSendFeedback.UseVisualStyleBackColor = true; this.flowPanel.Controls.Add(this.labelDataCollectionMessage);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 209);
this.flowPanel.TabIndex = 2;
this.flowPanel.WrapContents = false;
// //
// TabSettingsFeedback // TabSettingsFeedback
// //
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.panelFeedback); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.labelFeedback);
this.Name = "TabSettingsFeedback"; this.Name = "TabSettingsFeedback";
this.Size = new System.Drawing.Size(340, 122); this.Size = new System.Drawing.Size(340, 227);
this.panelFeedback.ResumeLayout(false); this.panelDataCollection.ResumeLayout(false);
this.panelFeedback.PerformLayout(); this.panelDataCollection.PerformLayout();
this.flowPanel.ResumeLayout(false);
this.flowPanel.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.Panel panelFeedback; private System.Windows.Forms.Panel panelDataCollection;
private System.Windows.Forms.CheckBox checkDataCollection; private System.Windows.Forms.CheckBox checkDataCollection;
private System.Windows.Forms.Label labelDataCollection; private System.Windows.Forms.Label labelDataCollection;
private System.Windows.Forms.Label labelFeedback; private System.Windows.Forms.Label labelFeedback;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.LinkLabel labelDataCollectionLink; private System.Windows.Forms.LinkLabel labelDataCollectionLink;
private System.Windows.Forms.Button btnSendFeedback; private System.Windows.Forms.Button btnSendFeedback;
private System.Windows.Forms.Button btnViewReport;
private System.Windows.Forms.Label labelDataCollectionMessage;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
} }
} }

View File

@@ -1,22 +1,40 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Core.Other.Analytics;
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsFeedback : BaseTabSettings{ sealed partial class TabSettingsFeedback : BaseTabSettings{
public TabSettingsFeedback(){ private readonly AnalyticsFile analyticsFile;
private readonly AnalyticsReportGenerator.ExternalInfo analyticsInfo;
private readonly PluginManager plugins;
public TabSettingsFeedback(AnalyticsManager analytics, AnalyticsReportGenerator.ExternalInfo analyticsInfo, PluginManager plugins){
InitializeComponent(); InitializeComponent();
this.analyticsFile = analytics?.File ?? AnalyticsFile.Load(Program.AnalyticsFilePath);
this.analyticsInfo = analyticsInfo;
this.plugins = plugins;
checkDataCollection.Checked = Config.AllowDataCollection; checkDataCollection.Checked = Config.AllowDataCollection;
if (analytics != null){
string collectionTime = analyticsFile.LastCollectionMessage;
labelDataCollectionMessage.Text = string.IsNullOrEmpty(collectionTime) ? "No collection yet" : "Last collection: "+collectionTime;
}
} }
public override void OnReady(){ public override void OnReady(){
btnSendFeedback.Click += btnSendFeedback_Click; btnSendFeedback.Click += btnSendFeedback_Click;
checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged; checkDataCollection.CheckedChanged += checkDataCollection_CheckedChanged;
labelDataCollectionLink.LinkClicked += labelDataCollectionLink_LinkClicked;
btnViewReport.Click += btnViewReport_Click;
} }
private void btnSendFeedback_Click(object sender, EventArgs e){ private void btnSendFeedback_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/issues/new"); BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/issues/new");
} }
private void checkDataCollection_CheckedChanged(object sender, EventArgs e){ private void checkDataCollection_CheckedChanged(object sender, EventArgs e){
@@ -24,7 +42,13 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){ private void labelDataCollectionLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e){
BrowserUtils.OpenExternalBrowserUnsafe("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data"); BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki/Send-anonymous-data");
}
private void btnViewReport_Click(object sender, EventArgs e){
using(DialogSettingsAnalytics dialog = new DialogSettingsAnalytics(AnalyticsReportGenerator.Create(analyticsFile, analyticsInfo, plugins))){
dialog.ShowDialog();
}
} }
} }
} }

View File

@@ -26,7 +26,6 @@
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.checkExpandLinks = new System.Windows.Forms.CheckBox(); this.checkExpandLinks = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox(); this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.btnCheckUpdates = new System.Windows.Forms.Button(); this.btnCheckUpdates = new System.Windows.Forms.Button();
this.labelZoomValue = new System.Windows.Forms.Label(); this.labelZoomValue = new System.Windows.Forms.Label();
@@ -37,107 +36,89 @@
this.labelZoom = new System.Windows.Forms.Label(); this.labelZoom = new System.Windows.Forms.Label();
this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components); this.zoomUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.labelUI = new System.Windows.Forms.Label(); this.labelUI = new System.Windows.Forms.Label();
this.panelUI = new System.Windows.Forms.Panel(); this.panelZoom = new System.Windows.Forms.Panel();
this.panelUpdates = new System.Windows.Forms.Panel(); this.checkAnimatedAvatars = new System.Windows.Forms.CheckBox();
this.labelUpdates = new System.Windows.Forms.Label(); this.labelUpdates = new System.Windows.Forms.Label();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).BeginInit();
this.panelUI.SuspendLayout(); this.panelZoom.SuspendLayout();
this.panelUpdates.SuspendLayout(); this.flowPanel.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// checkExpandLinks // checkExpandLinks
// //
this.checkExpandLinks.AutoSize = true; this.checkExpandLinks.AutoSize = true;
this.checkExpandLinks.Location = new System.Drawing.Point(6, 5); this.checkExpandLinks.Location = new System.Drawing.Point(6, 26);
this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 0;
this.checkExpandLinks.Text = "Expand Links When Hovered"; this.checkExpandLinks.Text = "Expand Links When Hovered";
this.toolTip.SetToolTip(this.checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
this.checkExpandLinks.UseVisualStyleBackColor = true; this.checkExpandLinks.UseVisualStyleBackColor = true;
// //
// checkSpellCheck
//
this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Location = new System.Drawing.Point(6, 97);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 4;
this.checkSpellCheck.Text = "Enable Spell Check";
this.toolTip.SetToolTip(this.checkSpellCheck, "Underlines words that are spelled incorrectly.");
this.checkSpellCheck.UseVisualStyleBackColor = true;
//
// checkUpdateNotifications // checkUpdateNotifications
// //
this.checkUpdateNotifications.AutoSize = true; this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 5); this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 245);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 0;
this.checkUpdateNotifications.Text = "Check Updates Automatically"; this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.toolTip.SetToolTip(this.checkUpdateNotifications, "Checks for updates every hour.\r\nIf an update is dismissed, it will not appear again.");
this.checkUpdateNotifications.UseVisualStyleBackColor = true; this.checkUpdateNotifications.UseVisualStyleBackColor = true;
// //
// btnCheckUpdates // btnCheckUpdates
// //
this.btnCheckUpdates.Location = new System.Drawing.Point(5, 28); this.btnCheckUpdates.Location = new System.Drawing.Point(5, 268);
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 = 1;
this.btnCheckUpdates.Text = "Check Updates Now"; this.btnCheckUpdates.Text = "Check Updates Now";
this.toolTip.SetToolTip(this.btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
this.btnCheckUpdates.UseVisualStyleBackColor = true; this.btnCheckUpdates.UseVisualStyleBackColor = true;
// //
// labelZoomValue // labelZoomValue
// //
this.labelZoomValue.BackColor = System.Drawing.Color.Transparent; this.labelZoomValue.BackColor = System.Drawing.Color.Transparent;
this.labelZoomValue.Location = new System.Drawing.Point(147, 146); this.labelZoomValue.Location = new System.Drawing.Point(147, 4);
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 = 7; this.labelZoomValue.TabIndex = 8;
this.labelZoomValue.Text = "100%"; this.labelZoomValue.Text = "100%";
this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight; this.labelZoomValue.TextAlign = System.Drawing.ContentAlignment.TopRight;
this.toolTip.SetToolTip(this.labelZoomValue, "Changes the zoom level.\r\nAlso affects notifications and screenshots.");
// //
// checkSwitchAccountSelectors // checkSwitchAccountSelectors
// //
this.checkSwitchAccountSelectors.AutoSize = true; this.checkSwitchAccountSelectors.AutoSize = true;
this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(6, 28); this.checkSwitchAccountSelectors.Location = new System.Drawing.Point(6, 49);
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 = 1;
this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts"; this.checkSwitchAccountSelectors.Text = "Shift Selects Multiple Accounts";
this.toolTip.SetToolTip(this.checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true; this.checkSwitchAccountSelectors.UseVisualStyleBackColor = true;
// //
// checkBestImageQuality // checkBestImageQuality
// //
this.checkBestImageQuality.AutoSize = true; this.checkBestImageQuality.AutoSize = true;
this.checkBestImageQuality.Location = new System.Drawing.Point(6, 74); this.checkBestImageQuality.Location = new System.Drawing.Point(6, 95);
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 = 3;
this.checkBestImageQuality.Text = "Best Image Quality"; this.checkBestImageQuality.Text = "Best Image Quality";
this.toolTip.SetToolTip(this.checkBestImageQuality, "When right-clicking a tweet image, the context menu options\r\nwill use links to the original image size (:orig in the URL).");
this.checkBestImageQuality.UseVisualStyleBackColor = true; this.checkBestImageQuality.UseVisualStyleBackColor = true;
// //
// checkOpenSearchInFirstColumn // checkOpenSearchInFirstColumn
// //
this.checkOpenSearchInFirstColumn.AutoSize = true; this.checkOpenSearchInFirstColumn.AutoSize = true;
this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 51); this.checkOpenSearchInFirstColumn.Location = new System.Drawing.Point(6, 72);
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 = 2;
this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column"; this.checkOpenSearchInFirstColumn.Text = "Add Search Columns Before First Column";
this.toolTip.SetToolTip(this.checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true; this.checkOpenSearchInFirstColumn.UseVisualStyleBackColor = true;
// //
// trackBarZoom // trackBarZoom
@@ -145,24 +126,24 @@
this.trackBarZoom.AutoSize = false; this.trackBarZoom.AutoSize = false;
this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control; this.trackBarZoom.BackColor = System.Drawing.SystemColors.Control;
this.trackBarZoom.LargeChange = 25; this.trackBarZoom.LargeChange = 25;
this.trackBarZoom.Location = new System.Drawing.Point(3, 145); this.trackBarZoom.Location = new System.Drawing.Point(3, 3);
this.trackBarZoom.Maximum = 200; this.trackBarZoom.Maximum = 200;
this.trackBarZoom.Minimum = 50; this.trackBarZoom.Minimum = 50;
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 = 6; this.trackBarZoom.TabIndex = 7;
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, 129); this.labelZoom.Location = new System.Drawing.Point(3, 150);
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 = 5; this.labelZoom.TabIndex = 6;
this.labelZoom.Text = "Zoom"; this.labelZoom.Text = "Zoom";
// //
// zoomUpdateTimer // zoomUpdateTimer
@@ -174,69 +155,81 @@
// //
this.labelUI.AutoSize = true; this.labelUI.AutoSize = true;
this.labelUI.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelUI.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelUI.Location = new System.Drawing.Point(6, 8); this.labelUI.Location = new System.Drawing.Point(0, 0);
this.labelUI.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.labelUI.Margin = new System.Windows.Forms.Padding(0);
this.labelUI.Name = "labelUI"; this.labelUI.Name = "labelUI";
this.labelUI.Size = new System.Drawing.Size(111, 20); this.labelUI.Size = new System.Drawing.Size(111, 20);
this.labelUI.TabIndex = 0; this.labelUI.TabIndex = 0;
this.labelUI.Text = "User Interface"; this.labelUI.Text = "User Interface";
// //
// panelUI // panelZoom
// //
this.panelUI.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelZoom.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right))); this.panelZoom.Controls.Add(this.trackBarZoom);
this.panelUI.Controls.Add(this.checkOpenSearchInFirstColumn); this.panelZoom.Controls.Add(this.labelZoomValue);
this.panelUI.Controls.Add(this.checkBestImageQuality); this.panelZoom.Location = new System.Drawing.Point(0, 163);
this.panelUI.Controls.Add(this.checkExpandLinks); this.panelZoom.Margin = new System.Windows.Forms.Padding(0);
this.panelUI.Controls.Add(this.checkSwitchAccountSelectors); this.panelZoom.Name = "panelZoom";
this.panelUI.Controls.Add(this.checkSpellCheck); this.panelZoom.Size = new System.Drawing.Size(322, 36);
this.panelUI.Controls.Add(this.labelZoom); this.panelZoom.TabIndex = 1;
this.panelUI.Controls.Add(this.trackBarZoom);
this.panelUI.Controls.Add(this.labelZoomValue);
this.panelUI.Location = new System.Drawing.Point(9, 31);
this.panelUI.Name = "panelUI";
this.panelUI.Size = new System.Drawing.Size(322, 179);
this.panelUI.TabIndex = 1;
// //
// panelUpdates // checkAnimatedAvatars
// //
this.panelUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.checkAnimatedAvatars.AutoSize = true;
| System.Windows.Forms.AnchorStyles.Right))); this.checkAnimatedAvatars.Location = new System.Drawing.Point(6, 118);
this.panelUpdates.Controls.Add(this.checkUpdateNotifications); this.checkAnimatedAvatars.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.panelUpdates.Controls.Add(this.btnCheckUpdates); this.checkAnimatedAvatars.Name = "checkAnimatedAvatars";
this.panelUpdates.Location = new System.Drawing.Point(9, 257); this.checkAnimatedAvatars.Size = new System.Drawing.Size(145, 17);
this.panelUpdates.Name = "panelUpdates"; this.checkAnimatedAvatars.TabIndex = 4;
this.panelUpdates.Size = new System.Drawing.Size(322, 55); this.checkAnimatedAvatars.Text = "Enable Animated Avatars";
this.panelUpdates.TabIndex = 3; this.checkAnimatedAvatars.UseVisualStyleBackColor = true;
// //
// labelUpdates // labelUpdates
// //
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(6, 234); this.labelUpdates.Location = new System.Drawing.Point(0, 219);
this.labelUpdates.Margin = new System.Windows.Forms.Padding(0, 21, 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 = 2;
this.labelUpdates.Text = "Updates"; this.labelUpdates.Text = "Updates";
// //
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Controls.Add(this.labelUI);
this.flowPanel.Controls.Add(this.checkExpandLinks);
this.flowPanel.Controls.Add(this.checkSwitchAccountSelectors);
this.flowPanel.Controls.Add(this.checkOpenSearchInFirstColumn);
this.flowPanel.Controls.Add(this.checkBestImageQuality);
this.flowPanel.Controls.Add(this.checkAnimatedAvatars);
this.flowPanel.Controls.Add(this.labelZoom);
this.flowPanel.Controls.Add(this.panelZoom);
this.flowPanel.Controls.Add(this.labelUpdates);
this.flowPanel.Controls.Add(this.checkUpdateNotifications);
this.flowPanel.Controls.Add(this.btnCheckUpdates);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 296);
this.flowPanel.TabIndex = 4;
this.flowPanel.WrapContents = false;
//
// 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.labelUpdates); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.panelUpdates);
this.Controls.Add(this.panelUI);
this.Controls.Add(this.labelUI);
this.Name = "TabSettingsGeneral"; this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(340, 322); this.Size = new System.Drawing.Size(340, 314);
((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarZoom)).EndInit();
this.panelUI.ResumeLayout(false); this.panelZoom.ResumeLayout(false);
this.panelUI.PerformLayout(); this.flowPanel.ResumeLayout(false);
this.panelUpdates.ResumeLayout(false); this.flowPanel.PerformLayout();
this.panelUpdates.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
@@ -244,7 +237,6 @@
private System.Windows.Forms.CheckBox checkExpandLinks; private System.Windows.Forms.CheckBox checkExpandLinks;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.CheckBox checkUpdateNotifications; private System.Windows.Forms.CheckBox checkUpdateNotifications;
private System.Windows.Forms.Button btnCheckUpdates; private System.Windows.Forms.Button btnCheckUpdates;
private System.Windows.Forms.Label labelZoom; private System.Windows.Forms.Label labelZoom;
@@ -253,10 +245,11 @@
private System.Windows.Forms.Timer zoomUpdateTimer; private System.Windows.Forms.Timer zoomUpdateTimer;
private System.Windows.Forms.CheckBox checkSwitchAccountSelectors; private System.Windows.Forms.CheckBox checkSwitchAccountSelectors;
private System.Windows.Forms.Label labelUI; private System.Windows.Forms.Label labelUI;
private System.Windows.Forms.Panel panelUI; private System.Windows.Forms.Panel panelZoom;
private System.Windows.Forms.Panel panelUpdates;
private System.Windows.Forms.Label labelUpdates; private System.Windows.Forms.Label labelUpdates;
private System.Windows.Forms.CheckBox checkBestImageQuality; private System.Windows.Forms.CheckBox checkBestImageQuality;
private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn; private System.Windows.Forms.CheckBox checkOpenSearchInFirstColumn;
private System.Windows.Forms.CheckBox checkAnimatedAvatars;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
} }
} }

View File

@@ -1,20 +1,35 @@
using System; using System;
using TweetDuck.Core.Controls; using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling.General;
using TweetDuck.Updates; using TweetDuck.Updates;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsGeneral : BaseTabSettings{ sealed partial class TabSettingsGeneral : BaseTabSettings{
private readonly FormBrowser browser;
private readonly UpdateHandler updates; private readonly UpdateHandler updates;
private int updateCheckEventId = -1; private int updateCheckEventId = -1;
public TabSettingsGeneral(UpdateHandler updates){ public TabSettingsGeneral(FormBrowser browser, UpdateHandler updates){
InitializeComponent(); InitializeComponent();
this.browser = browser;
this.updates = updates; this.updates = updates;
this.updates.CheckFinished += updates_CheckFinished; this.updates.CheckFinished += updates_CheckFinished;
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished; Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
toolTip.SetToolTip(checkExpandLinks, "Expands links inside the tweets. If disabled,\r\nthe full links show up in a tooltip instead.");
toolTip.SetToolTip(checkSwitchAccountSelectors, "When (re)tweeting, click to select a single account or hold Shift to\r\nselect multiple accounts, instead of TweetDeck\'s default behavior.");
toolTip.SetToolTip(checkOpenSearchInFirstColumn, "By default, TweetDeck adds Search columns at the end.\r\nThis option makes them appear before the first column instead.");
toolTip.SetToolTip(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(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(btnCheckUpdates, "Forces an update check, even for updates that had been dismissed.");
trackBarZoom.SetValueSafe(Config.ZoomLevel); trackBarZoom.SetValueSafe(Config.ZoomLevel);
labelZoomValue.Text = trackBarZoom.Value+"%"; labelZoomValue.Text = trackBarZoom.Value+"%";
@@ -22,7 +37,7 @@ namespace TweetDuck.Core.Other.Settings{
checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors; checkSwitchAccountSelectors.Checked = Config.SwitchAccountSelectors;
checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn; checkOpenSearchInFirstColumn.Checked = Config.OpenSearchInFirstColumn;
checkBestImageQuality.Checked = Config.BestImageQuality; checkBestImageQuality.Checked = Config.BestImageQuality;
checkSpellCheck.Checked = Config.EnableSpellCheck; checkAnimatedAvatars.Checked = Config.EnableAnimatedImages;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck; checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
} }
@@ -32,7 +47,7 @@ namespace TweetDuck.Core.Other.Settings{
checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged; checkSwitchAccountSelectors.CheckedChanged += checkSwitchAccountSelectors_CheckedChanged;
checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged; checkOpenSearchInFirstColumn.CheckedChanged += checkOpenSearchInFirstColumn_CheckedChanged;
checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged; checkBestImageQuality.CheckedChanged += checkBestImageQuality_CheckedChanged;
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged; checkAnimatedAvatars.CheckedChanged += checkAnimatedAvatars_CheckedChanged;
trackBarZoom.ValueChanged += trackBarZoom_ValueChanged; trackBarZoom.ValueChanged += trackBarZoom_ValueChanged;
checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged; checkUpdateNotifications.CheckedChanged += checkUpdateNotifications_CheckedChanged;
@@ -59,9 +74,9 @@ namespace TweetDuck.Core.Other.Settings{
Config.BestImageQuality = checkBestImageQuality.Checked; Config.BestImageQuality = checkBestImageQuality.Checked;
} }
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){ private void checkAnimatedAvatars_CheckedChanged(object sender, EventArgs e){
Config.EnableSpellCheck = checkSpellCheck.Checked; Config.EnableAnimatedImages = checkAnimatedAvatars.Checked;
PromptRestart(); BrowserProcessHandler.UpdatePrefs().ContinueWith(task => browser.ReloadColumns());
} }
private void trackBarZoom_ValueChanged(object sender, EventArgs e){ private void trackBarZoom_ValueChanged(object sender, EventArgs e){

View File

@@ -0,0 +1,155 @@
namespace TweetDuck.Core.Other.Settings {
partial class TabSettingsLocales {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.labelLocales = new System.Windows.Forms.Label();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.labelAppLanguage = new System.Windows.Forms.Label();
this.comboBoxAppLocale = new System.Windows.Forms.ComboBox();
this.labelTranslations = new System.Windows.Forms.Label();
this.labelTranslationTarget = new System.Windows.Forms.Label();
this.comboBoxTranslationTarget = new System.Windows.Forms.ComboBox();
this.flowPanel.SuspendLayout();
this.SuspendLayout();
//
// checkSpellCheck
//
this.checkSpellCheck.AutoSize = true;
this.checkSpellCheck.Location = new System.Drawing.Point(6, 26);
this.checkSpellCheck.Margin = new System.Windows.Forms.Padding(6, 6, 3, 3);
this.checkSpellCheck.Name = "checkSpellCheck";
this.checkSpellCheck.Size = new System.Drawing.Size(119, 17);
this.checkSpellCheck.TabIndex = 5;
this.checkSpellCheck.Text = "Enable Spell Check";
this.checkSpellCheck.UseVisualStyleBackColor = true;
//
// labelLocales
//
this.labelLocales.AutoSize = true;
this.labelLocales.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelLocales.Location = new System.Drawing.Point(0, 0);
this.labelLocales.Margin = new System.Windows.Forms.Padding(0);
this.labelLocales.Name = "labelLocales";
this.labelLocales.Size = new System.Drawing.Size(64, 20);
this.labelLocales.TabIndex = 0;
this.labelLocales.Text = "Locales";
//
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Controls.Add(this.labelLocales);
this.flowPanel.Controls.Add(this.checkSpellCheck);
this.flowPanel.Controls.Add(this.labelAppLanguage);
this.flowPanel.Controls.Add(this.comboBoxAppLocale);
this.flowPanel.Controls.Add(this.labelTranslations);
this.flowPanel.Controls.Add(this.labelTranslationTarget);
this.flowPanel.Controls.Add(this.comboBoxTranslationTarget);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 193);
this.flowPanel.TabIndex = 4;
this.flowPanel.WrapContents = false;
//
// labelAppLanguage
//
this.labelAppLanguage.AutoSize = true;
this.labelAppLanguage.Location = new System.Drawing.Point(3, 58);
this.labelAppLanguage.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelAppLanguage.Name = "labelAppLanguage";
this.labelAppLanguage.Size = new System.Drawing.Size(77, 13);
this.labelAppLanguage.TabIndex = 11;
this.labelAppLanguage.Text = "App Language";
//
// comboBoxAppLocale
//
this.comboBoxAppLocale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxAppLocale.FormattingEnabled = true;
this.comboBoxAppLocale.Location = new System.Drawing.Point(5, 74);
this.comboBoxAppLocale.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxAppLocale.Name = "comboBoxAppLocale";
this.comboBoxAppLocale.Size = new System.Drawing.Size(311, 21);
this.comboBoxAppLocale.TabIndex = 9;
//
// labelTranslations
//
this.labelTranslations.AutoSize = true;
this.labelTranslations.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTranslations.Location = new System.Drawing.Point(0, 118);
this.labelTranslations.Margin = new System.Windows.Forms.Padding(0, 20, 0, 0);
this.labelTranslations.Name = "labelTranslations";
this.labelTranslations.Size = new System.Drawing.Size(116, 20);
this.labelTranslations.TabIndex = 10;
this.labelTranslations.Text = "Bing Translator";
//
// labelTranslationTarget
//
this.labelTranslationTarget.AutoSize = true;
this.labelTranslationTarget.Location = new System.Drawing.Point(3, 150);
this.labelTranslationTarget.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelTranslationTarget.Name = "labelTranslationTarget";
this.labelTranslationTarget.Size = new System.Drawing.Size(89, 13);
this.labelTranslationTarget.TabIndex = 8;
this.labelTranslationTarget.Text = "Target Language";
//
// comboBoxTranslationTarget
//
this.comboBoxTranslationTarget.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTranslationTarget.FormattingEnabled = true;
this.comboBoxTranslationTarget.Location = new System.Drawing.Point(5, 166);
this.comboBoxTranslationTarget.Margin = new System.Windows.Forms.Padding(5, 3, 3, 3);
this.comboBoxTranslationTarget.Name = "comboBoxTranslationTarget";
this.comboBoxTranslationTarget.Size = new System.Drawing.Size(311, 21);
this.comboBoxTranslationTarget.TabIndex = 7;
//
// TabSettingsLocales
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.flowPanel);
this.Name = "TabSettingsLocales";
this.Size = new System.Drawing.Size(340, 211);
this.flowPanel.ResumeLayout(false);
this.flowPanel.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.Label labelLocales;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.ComboBox comboBoxTranslationTarget;
private System.Windows.Forms.Label labelTranslationTarget;
private System.Windows.Forms.ComboBox comboBoxAppLocale;
private System.Windows.Forms.Label labelTranslations;
private System.Windows.Forms.Label labelAppLanguage;
}
}

View File

@@ -0,0 +1,53 @@
using System;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils;
namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsLocales : BaseTabSettings{
public TabSettingsLocales(){
InitializeComponent();
toolTip.SetToolTip(checkSpellCheck, "Underlines words that are spelled incorrectly.");
toolTip.SetToolTip(comboBoxAppLocale, "Language used for spell check and context menu items.");
toolTip.SetToolTip(comboBoxTranslationTarget, "Language tweets are translated into.");
checkSpellCheck.Checked = Config.EnableSpellCheck;
try{
foreach(LocaleUtils.Item item in LocaleUtils.ChromiumLocales){
comboBoxAppLocale.Items.Add(item);
}
}catch{
comboBoxAppLocale.Items.Add(new LocaleUtils.Item("en-US"));
}
comboBoxAppLocale.SelectedItem = new LocaleUtils.Item(Config.AppLocale);
foreach(LocaleUtils.Item item in LocaleUtils.TweetDeckTranslationLocales){
comboBoxTranslationTarget.Items.Add(item);
}
comboBoxTranslationTarget.SelectedItem = new LocaleUtils.Item(Config.TranslationTarget);
}
public override void OnReady(){
checkSpellCheck.CheckedChanged += checkSpellCheck_CheckedChanged;
comboBoxAppLocale.SelectedValueChanged += comboBoxAppLocale_SelectedValueChanged;
comboBoxTranslationTarget.SelectedValueChanged += comboBoxTranslationTarget_SelectedValueChanged;
}
private void checkSpellCheck_CheckedChanged(object sender, EventArgs e){
Config.EnableSpellCheck = checkSpellCheck.Checked;
BrowserProcessHandler.UpdatePrefs();
}
private void comboBoxAppLocale_SelectedValueChanged(object sender, EventArgs e){
Config.AppLocale = (comboBoxAppLocale.SelectedItem as LocaleUtils.Item)?.Code;
PromptRestart();
}
private void comboBoxTranslationTarget_SelectedValueChanged(object sender, EventArgs e){
Config.TranslationTarget = (comboBoxTranslationTarget.SelectedItem as LocaleUtils.Item)?.Code;
}
}
}

View File

@@ -51,7 +51,8 @@
this.radioSizeAuto = new System.Windows.Forms.RadioButton(); this.radioSizeAuto = new System.Windows.Forms.RadioButton();
this.radioSizeCustom = new System.Windows.Forms.RadioButton(); this.radioSizeCustom = new System.Windows.Forms.RadioButton();
this.labelGeneral = new System.Windows.Forms.Label(); this.labelGeneral = new System.Windows.Forms.Label();
this.panelGeneral = new System.Windows.Forms.Panel(); this.panelEdgeDistance = new System.Windows.Forms.Panel();
this.checkMediaPreviews = new System.Windows.Forms.CheckBox();
this.labelScrollSpeedValue = new System.Windows.Forms.Label(); this.labelScrollSpeedValue = new System.Windows.Forms.Label();
this.trackBarScrollSpeed = new System.Windows.Forms.TrackBar(); this.trackBarScrollSpeed = new System.Windows.Forms.TrackBar();
this.labelScrollSpeed = new System.Windows.Forms.Label(); this.labelScrollSpeed = new System.Windows.Forms.Label();
@@ -61,22 +62,25 @@
this.labelDuration = new System.Windows.Forms.Label(); this.labelDuration = new System.Windows.Forms.Label();
this.labelTimer = new System.Windows.Forms.Label(); this.labelTimer = new System.Windows.Forms.Label();
this.labelSize = new System.Windows.Forms.Label(); this.labelSize = new System.Windows.Forms.Label();
this.panelMiscellaneous = new System.Windows.Forms.Panel(); this.panelSize = new System.Windows.Forms.Panel();
this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components); this.durationUpdateTimer = new System.Windows.Forms.Timer(this.components);
this.checkMediaPreviews = new System.Windows.Forms.CheckBox(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.panelScrollSpeed = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.tableLayoutDurationButtons.SuspendLayout(); this.tableLayoutDurationButtons.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).BeginInit();
this.panelGeneral.SuspendLayout(); this.panelEdgeDistance.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).BeginInit();
this.panelLocation.SuspendLayout(); this.panelLocation.SuspendLayout();
this.panelTimer.SuspendLayout(); this.panelTimer.SuspendLayout();
this.panelMiscellaneous.SuspendLayout(); this.panelSize.SuspendLayout();
this.flowPanel.SuspendLayout();
this.panelScrollSpeed.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// labelEdgeDistanceValue // labelEdgeDistanceValue
// //
this.labelEdgeDistanceValue.Location = new System.Drawing.Point(147, 129); this.labelEdgeDistanceValue.Location = new System.Drawing.Point(145, 4);
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);
@@ -87,7 +91,7 @@
// labelDisplay // labelDisplay
// //
this.labelDisplay.AutoSize = true; this.labelDisplay.AutoSize = true;
this.labelDisplay.Location = new System.Drawing.Point(3, 60); this.labelDisplay.Location = new System.Drawing.Point(3, 451);
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);
@@ -96,11 +100,9 @@
// //
// comboBoxDisplay // comboBoxDisplay
// //
this.comboBoxDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDisplay.FormattingEnabled = true; this.comboBoxDisplay.FormattingEnabled = true;
this.comboBoxDisplay.Location = new System.Drawing.Point(5, 76); this.comboBoxDisplay.Location = new System.Drawing.Point(5, 467);
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);
@@ -109,7 +111,7 @@
// labelEdgeDistance // labelEdgeDistance
// //
this.labelEdgeDistance.AutoSize = true; this.labelEdgeDistance.AutoSize = true;
this.labelEdgeDistance.Location = new System.Drawing.Point(3, 112); this.labelEdgeDistance.Location = new System.Drawing.Point(3, 503);
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);
@@ -125,7 +127,6 @@
this.radioLocCustom.TabIndex = 4; this.radioLocCustom.TabIndex = 4;
this.radioLocCustom.TabStop = true; this.radioLocCustom.TabStop = true;
this.radioLocCustom.Text = "Custom"; this.radioLocCustom.Text = "Custom";
this.toolTip.SetToolTip(this.radioLocCustom, "Drag the example notification window to the desired location.");
this.radioLocCustom.UseVisualStyleBackColor = true; this.radioLocCustom.UseVisualStyleBackColor = true;
// //
// radioLocBR // radioLocBR
@@ -176,7 +177,7 @@
// //
this.trackBarEdgeDistance.AutoSize = false; this.trackBarEdgeDistance.AutoSize = false;
this.trackBarEdgeDistance.LargeChange = 8; this.trackBarEdgeDistance.LargeChange = 8;
this.trackBarEdgeDistance.Location = new System.Drawing.Point(5, 128); this.trackBarEdgeDistance.Location = new System.Drawing.Point(3, 3);
this.trackBarEdgeDistance.Maximum = 40; this.trackBarEdgeDistance.Maximum = 40;
this.trackBarEdgeDistance.Minimum = 8; this.trackBarEdgeDistance.Minimum = 8;
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance"; this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
@@ -195,7 +196,7 @@
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationMedium, 0, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationMedium, 0, 0);
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationLong, 1, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationLong, 1, 0);
this.tableLayoutDurationButtons.Controls.Add(this.btnDurationShort, 0, 0); this.tableLayoutDurationButtons.Controls.Add(this.btnDurationShort, 0, 0);
this.tableLayoutDurationButtons.Location = new System.Drawing.Point(3, 113); this.tableLayoutDurationButtons.Location = new System.Drawing.Point(3, 320);
this.tableLayoutDurationButtons.Name = "tableLayoutDurationButtons"; this.tableLayoutDurationButtons.Name = "tableLayoutDurationButtons";
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));
@@ -250,19 +251,18 @@
// labelDurationValue // labelDurationValue
// //
this.labelDurationValue.BackColor = System.Drawing.Color.Transparent; this.labelDurationValue.BackColor = System.Drawing.Color.Transparent;
this.labelDurationValue.Location = new System.Drawing.Point(147, 77); this.labelDurationValue.Location = new System.Drawing.Point(147, 4);
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 = 4;
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;
this.toolTip.SetToolTip(this.labelDurationValue, "Milliseconds per character.");
// //
// trackBarDuration // trackBarDuration
// //
this.trackBarDuration.AutoSize = false; this.trackBarDuration.AutoSize = false;
this.trackBarDuration.Location = new System.Drawing.Point(3, 76); this.trackBarDuration.Location = new System.Drawing.Point(3, 3);
this.trackBarDuration.Maximum = 60; this.trackBarDuration.Maximum = 60;
this.trackBarDuration.Minimum = 10; this.trackBarDuration.Minimum = 10;
this.trackBarDuration.Name = "trackBarDuration"; this.trackBarDuration.Name = "trackBarDuration";
@@ -274,31 +274,29 @@
// checkSkipOnLinkClick // checkSkipOnLinkClick
// //
this.checkSkipOnLinkClick.AutoSize = true; this.checkSkipOnLinkClick.AutoSize = true;
this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 51); this.checkSkipOnLinkClick.Location = new System.Drawing.Point(6, 72);
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 = 2;
this.checkSkipOnLinkClick.Text = "Skip On Link Click"; this.checkSkipOnLinkClick.Text = "Skip On Link Click";
this.toolTip.SetToolTip(this.checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
this.checkSkipOnLinkClick.UseVisualStyleBackColor = true; this.checkSkipOnLinkClick.UseVisualStyleBackColor = true;
// //
// checkColumnName // checkColumnName
// //
this.checkColumnName.AutoSize = true; this.checkColumnName.AutoSize = true;
this.checkColumnName.Location = new System.Drawing.Point(6, 5); this.checkColumnName.Location = new System.Drawing.Point(6, 26);
this.checkColumnName.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 0;
this.checkColumnName.Text = "Display Column Name"; this.checkColumnName.Text = "Display Column Name";
this.toolTip.SetToolTip(this.checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window title.");
this.checkColumnName.UseVisualStyleBackColor = true; this.checkColumnName.UseVisualStyleBackColor = true;
// //
// labelIdlePause // labelIdlePause
// //
this.labelIdlePause.AutoSize = true; this.labelIdlePause.AutoSize = true;
this.labelIdlePause.Location = new System.Drawing.Point(3, 106); this.labelIdlePause.Location = new System.Drawing.Point(3, 127);
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);
@@ -309,42 +307,39 @@
// //
this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxIdlePause.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxIdlePause.FormattingEnabled = true; this.comboBoxIdlePause.FormattingEnabled = true;
this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 122); this.comboBoxIdlePause.Location = new System.Drawing.Point(5, 143);
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 = 5;
this.toolTip.SetToolTip(this.comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
// //
// checkNonIntrusive // checkNonIntrusive
// //
this.checkNonIntrusive.AutoSize = true; this.checkNonIntrusive.AutoSize = true;
this.checkNonIntrusive.Location = new System.Drawing.Point(6, 74); this.checkNonIntrusive.Location = new System.Drawing.Point(6, 95);
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 = 3;
this.checkNonIntrusive.Text = "Non-Intrusive Popups"; this.checkNonIntrusive.Text = "Non-Intrusive Popups";
this.toolTip.SetToolTip(this.checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks.");
this.checkNonIntrusive.UseVisualStyleBackColor = true; this.checkNonIntrusive.UseVisualStyleBackColor = true;
// //
// checkTimerCountDown // checkTimerCountDown
// //
this.checkTimerCountDown.AutoSize = true; this.checkTimerCountDown.AutoSize = true;
this.checkTimerCountDown.Location = new System.Drawing.Point(6, 28); this.checkTimerCountDown.Location = new System.Drawing.Point(6, 236);
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 = 1;
this.checkTimerCountDown.Text = "Timer Counts Down"; this.checkTimerCountDown.Text = "Timer Counts Down";
this.toolTip.SetToolTip(this.checkTimerCountDown, "The notification timer counts down instead of up.");
this.checkTimerCountDown.UseVisualStyleBackColor = true; this.checkTimerCountDown.UseVisualStyleBackColor = true;
// //
// checkNotificationTimer // checkNotificationTimer
// //
this.checkNotificationTimer.AutoSize = true; this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(6, 5); this.checkNotificationTimer.Location = new System.Drawing.Point(6, 213);
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 0;
@@ -353,57 +348,62 @@
// //
// radioSizeAuto // radioSizeAuto
// //
this.radioSizeAuto.Location = new System.Drawing.Point(6, 4); this.radioSizeAuto.Location = new System.Drawing.Point(5, 4);
this.radioSizeAuto.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.radioSizeAuto.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioSizeAuto.Name = "radioSizeAuto"; this.radioSizeAuto.Name = "radioSizeAuto";
this.radioSizeAuto.Size = new System.Drawing.Size(92, 17); this.radioSizeAuto.Size = new System.Drawing.Size(92, 17);
this.radioSizeAuto.TabIndex = 0; this.radioSizeAuto.TabIndex = 0;
this.radioSizeAuto.TabStop = true; this.radioSizeAuto.TabStop = true;
this.radioSizeAuto.Text = "Auto"; this.radioSizeAuto.Text = "Auto";
this.toolTip.SetToolTip(this.radioSizeAuto, "Notification size is based on the font size and browser zoom level.");
this.radioSizeAuto.UseVisualStyleBackColor = true; this.radioSizeAuto.UseVisualStyleBackColor = true;
// //
// radioSizeCustom // radioSizeCustom
// //
this.radioSizeCustom.Location = new System.Drawing.Point(106, 4); this.radioSizeCustom.Location = new System.Drawing.Point(105, 4);
this.radioSizeCustom.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3); this.radioSizeCustom.Margin = new System.Windows.Forms.Padding(5, 4, 3, 3);
this.radioSizeCustom.Name = "radioSizeCustom"; this.radioSizeCustom.Name = "radioSizeCustom";
this.radioSizeCustom.Size = new System.Drawing.Size(92, 17); this.radioSizeCustom.Size = new System.Drawing.Size(92, 17);
this.radioSizeCustom.TabIndex = 1; this.radioSizeCustom.TabIndex = 1;
this.radioSizeCustom.TabStop = true; this.radioSizeCustom.TabStop = true;
this.radioSizeCustom.Text = "Custom"; this.radioSizeCustom.Text = "Custom";
this.toolTip.SetToolTip(this.radioSizeCustom, "Resize the example notification window to the desired size.");
this.radioSizeCustom.UseVisualStyleBackColor = true; this.radioSizeCustom.UseVisualStyleBackColor = true;
// //
// labelGeneral // labelGeneral
// //
this.labelGeneral.AutoSize = true; this.labelGeneral.AutoSize = true;
this.labelGeneral.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelGeneral.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelGeneral.Location = new System.Drawing.Point(6, 8); this.labelGeneral.Location = new System.Drawing.Point(0, 0);
this.labelGeneral.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.labelGeneral.Margin = new System.Windows.Forms.Padding(0);
this.labelGeneral.Name = "labelGeneral"; this.labelGeneral.Name = "labelGeneral";
this.labelGeneral.Size = new System.Drawing.Size(66, 20); this.labelGeneral.Size = new System.Drawing.Size(66, 20);
this.labelGeneral.TabIndex = 0; this.labelGeneral.TabIndex = 0;
this.labelGeneral.Text = "General"; this.labelGeneral.Text = "General";
// //
// panelGeneral // panelEdgeDistance
// //
this.panelGeneral.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelEdgeDistance.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right))); this.panelEdgeDistance.Controls.Add(this.trackBarEdgeDistance);
this.panelGeneral.Controls.Add(this.checkMediaPreviews); this.panelEdgeDistance.Controls.Add(this.labelEdgeDistanceValue);
this.panelGeneral.Controls.Add(this.checkColumnName); this.panelEdgeDistance.Location = new System.Drawing.Point(0, 516);
this.panelGeneral.Controls.Add(this.checkSkipOnLinkClick); this.panelEdgeDistance.Margin = new System.Windows.Forms.Padding(0);
this.panelGeneral.Controls.Add(this.checkNonIntrusive); this.panelEdgeDistance.Name = "panelEdgeDistance";
this.panelGeneral.Controls.Add(this.labelIdlePause); this.panelEdgeDistance.Size = new System.Drawing.Size(322, 36);
this.panelGeneral.Controls.Add(this.comboBoxIdlePause); this.panelEdgeDistance.TabIndex = 1;
this.panelGeneral.Location = new System.Drawing.Point(9, 31); //
this.panelGeneral.Name = "panelGeneral"; // checkMediaPreviews
this.panelGeneral.Size = new System.Drawing.Size(322, 149); //
this.panelGeneral.TabIndex = 1; this.checkMediaPreviews.AutoSize = true;
this.checkMediaPreviews.Location = new System.Drawing.Point(6, 49);
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.checkMediaPreviews.Name = "checkMediaPreviews";
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17);
this.checkMediaPreviews.TabIndex = 1;
this.checkMediaPreviews.Text = "Show Media Previews";
this.checkMediaPreviews.UseVisualStyleBackColor = true;
// //
// labelScrollSpeedValue // labelScrollSpeedValue
// //
this.labelScrollSpeedValue.Location = new System.Drawing.Point(147, 53); this.labelScrollSpeedValue.Location = new System.Drawing.Point(145, 4);
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);
@@ -415,7 +415,7 @@
// //
this.trackBarScrollSpeed.AutoSize = false; this.trackBarScrollSpeed.AutoSize = false;
this.trackBarScrollSpeed.LargeChange = 25; this.trackBarScrollSpeed.LargeChange = 25;
this.trackBarScrollSpeed.Location = new System.Drawing.Point(5, 52); this.trackBarScrollSpeed.Location = new System.Drawing.Point(3, 3);
this.trackBarScrollSpeed.Maximum = 200; this.trackBarScrollSpeed.Maximum = 200;
this.trackBarScrollSpeed.Minimum = 25; this.trackBarScrollSpeed.Minimum = 25;
this.trackBarScrollSpeed.Name = "trackBarScrollSpeed"; this.trackBarScrollSpeed.Name = "trackBarScrollSpeed";
@@ -428,7 +428,7 @@
// labelScrollSpeed // labelScrollSpeed
// //
this.labelScrollSpeed.AutoSize = true; this.labelScrollSpeed.AutoSize = true;
this.labelScrollSpeed.Location = new System.Drawing.Point(3, 36); this.labelScrollSpeed.Location = new System.Drawing.Point(3, 629);
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);
@@ -439,8 +439,8 @@
// //
this.labelLocation.AutoSize = true; this.labelLocation.AutoSize = true;
this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelLocation.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelLocation.Location = new System.Drawing.Point(6, 395); this.labelLocation.Location = new System.Drawing.Point(0, 370);
this.labelLocation.Margin = new System.Windows.Forms.Padding(0, 21, 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 = 4;
@@ -448,42 +448,33 @@
// //
// panelLocation // panelLocation
// //
this.panelLocation.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelLocation.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right)));
this.panelLocation.Controls.Add(this.radioLocTL); this.panelLocation.Controls.Add(this.radioLocTL);
this.panelLocation.Controls.Add(this.labelDisplay);
this.panelLocation.Controls.Add(this.trackBarEdgeDistance);
this.panelLocation.Controls.Add(this.comboBoxDisplay);
this.panelLocation.Controls.Add(this.radioLocTR); this.panelLocation.Controls.Add(this.radioLocTR);
this.panelLocation.Controls.Add(this.labelEdgeDistance);
this.panelLocation.Controls.Add(this.radioLocBL); this.panelLocation.Controls.Add(this.radioLocBL);
this.panelLocation.Controls.Add(this.radioLocCustom); this.panelLocation.Controls.Add(this.radioLocCustom);
this.panelLocation.Controls.Add(this.radioLocBR); this.panelLocation.Controls.Add(this.radioLocBR);
this.panelLocation.Controls.Add(this.labelEdgeDistanceValue); this.panelLocation.Location = new System.Drawing.Point(0, 390);
this.panelLocation.Location = new System.Drawing.Point(9, 418); 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, 165); this.panelLocation.Size = new System.Drawing.Size(322, 49);
this.panelLocation.TabIndex = 5; this.panelLocation.TabIndex = 5;
// //
// panelTimer // panelTimer
// //
this.panelTimer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelTimer.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTimer.Controls.Add(this.labelDuration);
this.panelTimer.Controls.Add(this.checkNotificationTimer);
this.panelTimer.Controls.Add(this.tableLayoutDurationButtons);
this.panelTimer.Controls.Add(this.checkTimerCountDown);
this.panelTimer.Controls.Add(this.labelDurationValue); this.panelTimer.Controls.Add(this.labelDurationValue);
this.panelTimer.Controls.Add(this.trackBarDuration); this.panelTimer.Controls.Add(this.trackBarDuration);
this.panelTimer.Location = new System.Drawing.Point(9, 227); this.panelTimer.Location = new System.Drawing.Point(0, 281);
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, 144); this.panelTimer.Size = new System.Drawing.Size(322, 36);
this.panelTimer.TabIndex = 3; this.panelTimer.TabIndex = 3;
// //
// labelDuration // labelDuration
// //
this.labelDuration.AutoSize = true; this.labelDuration.AutoSize = true;
this.labelDuration.Location = new System.Drawing.Point(3, 60); this.labelDuration.Location = new System.Drawing.Point(3, 268);
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);
@@ -494,8 +485,8 @@
// //
this.labelTimer.AutoSize = true; this.labelTimer.AutoSize = true;
this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelTimer.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTimer.Location = new System.Drawing.Point(6, 204); this.labelTimer.Location = new System.Drawing.Point(0, 187);
this.labelTimer.Margin = new System.Windows.Forms.Padding(0, 21, 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 = 2;
@@ -505,73 +496,96 @@
// //
this.labelSize.AutoSize = true; this.labelSize.AutoSize = true;
this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelSize.Location = new System.Drawing.Point(6, 607); this.labelSize.Location = new System.Drawing.Point(0, 572);
this.labelSize.Margin = new System.Windows.Forms.Padding(0, 21, 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 = 6;
this.labelSize.Text = "Size"; this.labelSize.Text = "Size";
// //
// panelMiscellaneous // panelSize
// //
this.panelMiscellaneous.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelSize.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.panelMiscellaneous.Controls.Add(this.radioSizeCustom); this.panelSize.Controls.Add(this.radioSizeCustom);
this.panelMiscellaneous.Controls.Add(this.radioSizeAuto); this.panelSize.Controls.Add(this.radioSizeAuto);
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeedValue); this.panelSize.Location = new System.Drawing.Point(0, 592);
this.panelMiscellaneous.Controls.Add(this.trackBarScrollSpeed); this.panelSize.Margin = new System.Windows.Forms.Padding(0);
this.panelMiscellaneous.Controls.Add(this.labelScrollSpeed); this.panelSize.Name = "panelSize";
this.panelMiscellaneous.Location = new System.Drawing.Point(9, 630); this.panelSize.Size = new System.Drawing.Size(322, 25);
this.panelMiscellaneous.Name = "panelMiscellaneous"; this.panelSize.TabIndex = 7;
this.panelMiscellaneous.Size = new System.Drawing.Size(322, 92);
this.panelMiscellaneous.TabIndex = 7;
// //
// durationUpdateTimer // durationUpdateTimer
// //
this.durationUpdateTimer.Interval = 200; this.durationUpdateTimer.Interval = 200;
this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick); this.durationUpdateTimer.Tick += new System.EventHandler(this.durationUpdateTimer_Tick);
// //
// checkMediaPreviews // flowPanel
// //
this.checkMediaPreviews.AutoSize = true; this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.checkMediaPreviews.Location = new System.Drawing.Point(6, 28); | System.Windows.Forms.AnchorStyles.Left)
this.checkMediaPreviews.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3); | System.Windows.Forms.AnchorStyles.Right)));
this.checkMediaPreviews.Name = "checkMediaPreviews"; this.flowPanel.Controls.Add(this.labelGeneral);
this.checkMediaPreviews.Size = new System.Drawing.Size(131, 17); this.flowPanel.Controls.Add(this.checkColumnName);
this.checkMediaPreviews.TabIndex = 1; this.flowPanel.Controls.Add(this.checkMediaPreviews);
this.checkMediaPreviews.Text = "Show Media Previews"; this.flowPanel.Controls.Add(this.checkSkipOnLinkClick);
this.toolTip.SetToolTip(this.checkMediaPreviews, "Shows image and video thumbnails in the notification window."); this.flowPanel.Controls.Add(this.checkNonIntrusive);
this.checkMediaPreviews.UseVisualStyleBackColor = true; this.flowPanel.Controls.Add(this.labelIdlePause);
this.flowPanel.Controls.Add(this.comboBoxIdlePause);
this.flowPanel.Controls.Add(this.labelTimer);
this.flowPanel.Controls.Add(this.checkNotificationTimer);
this.flowPanel.Controls.Add(this.checkTimerCountDown);
this.flowPanel.Controls.Add(this.labelDuration);
this.flowPanel.Controls.Add(this.panelTimer);
this.flowPanel.Controls.Add(this.tableLayoutDurationButtons);
this.flowPanel.Controls.Add(this.labelLocation);
this.flowPanel.Controls.Add(this.panelLocation);
this.flowPanel.Controls.Add(this.labelDisplay);
this.flowPanel.Controls.Add(this.comboBoxDisplay);
this.flowPanel.Controls.Add(this.labelEdgeDistance);
this.flowPanel.Controls.Add(this.panelEdgeDistance);
this.flowPanel.Controls.Add(this.labelSize);
this.flowPanel.Controls.Add(this.panelSize);
this.flowPanel.Controls.Add(this.labelScrollSpeed);
this.flowPanel.Controls.Add(this.panelScrollSpeed);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 678);
this.flowPanel.TabIndex = 8;
this.flowPanel.WrapContents = false;
//
// panelScrollSpeed
//
this.panelScrollSpeed.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.panelScrollSpeed.Controls.Add(this.trackBarScrollSpeed);
this.panelScrollSpeed.Controls.Add(this.labelScrollSpeedValue);
this.panelScrollSpeed.Location = new System.Drawing.Point(0, 642);
this.panelScrollSpeed.Margin = new System.Windows.Forms.Padding(0);
this.panelScrollSpeed.Name = "panelScrollSpeed";
this.panelScrollSpeed.Size = new System.Drawing.Size(322, 36);
this.panelScrollSpeed.TabIndex = 9;
// //
// TabSettingsNotifications // TabSettingsNotifications
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelMiscellaneous); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.labelSize);
this.Controls.Add(this.labelTimer);
this.Controls.Add(this.panelLocation);
this.Controls.Add(this.labelLocation);
this.Controls.Add(this.panelGeneral);
this.Controls.Add(this.labelGeneral);
this.Controls.Add(this.panelTimer);
this.Name = "TabSettingsNotifications"; this.Name = "TabSettingsNotifications";
this.Size = new System.Drawing.Size(340, 731); this.Size = new System.Drawing.Size(340, 697);
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged); this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.tableLayoutDurationButtons.ResumeLayout(false); this.tableLayoutDurationButtons.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarDuration)).EndInit();
this.panelGeneral.ResumeLayout(false); this.panelEdgeDistance.ResumeLayout(false);
this.panelGeneral.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarScrollSpeed)).EndInit();
this.panelLocation.ResumeLayout(false); this.panelLocation.ResumeLayout(false);
this.panelLocation.PerformLayout();
this.panelTimer.ResumeLayout(false); this.panelTimer.ResumeLayout(false);
this.panelTimer.PerformLayout(); this.panelSize.ResumeLayout(false);
this.panelMiscellaneous.ResumeLayout(false); this.flowPanel.ResumeLayout(false);
this.panelMiscellaneous.PerformLayout(); this.flowPanel.PerformLayout();
this.panelScrollSpeed.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
@@ -601,7 +615,7 @@
private System.Windows.Forms.CheckBox checkTimerCountDown; private System.Windows.Forms.CheckBox checkTimerCountDown;
private System.Windows.Forms.CheckBox checkNotificationTimer; private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.Label labelGeneral; private System.Windows.Forms.Label labelGeneral;
private System.Windows.Forms.Panel panelGeneral; private System.Windows.Forms.Panel panelEdgeDistance;
private System.Windows.Forms.Label labelLocation; private System.Windows.Forms.Label labelLocation;
private System.Windows.Forms.Panel panelLocation; private System.Windows.Forms.Panel panelLocation;
private System.Windows.Forms.Panel panelTimer; private System.Windows.Forms.Panel panelTimer;
@@ -610,11 +624,13 @@
private System.Windows.Forms.TrackBar trackBarScrollSpeed; private System.Windows.Forms.TrackBar trackBarScrollSpeed;
private System.Windows.Forms.Label labelScrollSpeed; private System.Windows.Forms.Label labelScrollSpeed;
private System.Windows.Forms.Label labelSize; private System.Windows.Forms.Label labelSize;
private System.Windows.Forms.Panel panelMiscellaneous; private System.Windows.Forms.Panel panelSize;
private System.Windows.Forms.Label labelDuration; private System.Windows.Forms.Label labelDuration;
private System.Windows.Forms.Timer durationUpdateTimer; private System.Windows.Forms.Timer durationUpdateTimer;
private System.Windows.Forms.RadioButton radioSizeCustom; private System.Windows.Forms.RadioButton radioSizeCustom;
private System.Windows.Forms.RadioButton radioSizeAuto; private System.Windows.Forms.RadioButton radioSizeAuto;
private System.Windows.Forms.CheckBox checkMediaPreviews; private System.Windows.Forms.CheckBox checkMediaPreviews;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.Panel panelScrollSpeed;
} }
} }

View File

@@ -2,21 +2,22 @@
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.Notification.Example;
namespace TweetDuck.Core.Other.Settings{ namespace TweetDuck.Core.Other.Settings{
sealed partial class TabSettingsNotifications : BaseTabSettings{ sealed partial class TabSettingsNotifications : BaseTabSettings{
private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 }; private static readonly int[] IdlePauseSeconds = { 0, 30, 60, 120, 300 };
private readonly FormNotificationMain notification; private readonly FormNotificationExample notification;
public TabSettingsNotifications(FormNotificationMain notification){ public TabSettingsNotifications(FormNotificationExample notification){
InitializeComponent(); InitializeComponent();
this.notification = notification; this.notification = notification;
this.notification.Initialized += (sender, args) => { this.notification.Initialized += (sender, args) => {
this.InvokeAsyncSafe(() => { this.InvokeAsyncSafe(() => {
this.notification.ShowNotificationForSettings(true); this.notification.ShowExampleNotification(true);
this.notification.Move += notification_Move; this.notification.Move += notification_Move;
this.notification.ResizeEnd += notification_ResizeEnd; this.notification.ResizeEnd += notification_ResizeEnd;
}); });
@@ -25,6 +26,41 @@ namespace TweetDuck.Core.Other.Settings{
this.notification.Activated += notification_Activated; this.notification.Activated += notification_Activated;
this.notification.Show(); this.notification.Show();
toolTip.SetToolTip(checkColumnName, "Shows column name each notification originated\r\nfrom in the notification window title.");
toolTip.SetToolTip(checkMediaPreviews, "Shows image and video thumbnails in the notification window.");
toolTip.SetToolTip(checkSkipOnLinkClick, "Skips current notification when a link\r\ninside the notification is clicked.");
toolTip.SetToolTip(checkNonIntrusive, "When not idle and the cursor is within the notification window area,\r\nit will be delayed until the cursor moves away to prevent accidental clicks.");
toolTip.SetToolTip(comboBoxIdlePause, "Pauses new notifications after going idle for a set amount of time.");
toolTip.SetToolTip(checkTimerCountDown, "The notification timer counts down instead of up.");
toolTip.SetToolTip(labelDurationValue, "Milliseconds per character.");
toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
toolTip.SetToolTip(radioLocCustom, "Drag the example notification window to the desired location.");
toolTip.SetToolTip(radioSizeAuto, "Notification size is based on the font size and browser zoom level.");
toolTip.SetToolTip(radioSizeCustom, "Resize the example notification window to the desired size.");
checkColumnName.Checked = Config.DisplayNotificationColumn;
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
comboBoxIdlePause.Items.Add("Disabled");
comboBoxIdlePause.Items.Add("30 seconds");
comboBoxIdlePause.Items.Add("1 minute");
comboBoxIdlePause.Items.Add("2 minutes");
comboBoxIdlePause.Items.Add("5 minutes");
comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds));
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
switch(Config.NotificationPosition){ switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break; case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break; case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
@@ -34,23 +70,6 @@ namespace TweetDuck.Core.Other.Settings{
} }
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
switch(Config.NotificationSize){
case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break;
case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break;
}
toolTip.SetToolTip(trackBarDuration, toolTip.GetToolTip(labelDurationValue));
trackBarDuration.SetValueSafe(Config.NotificationDurationValue);
labelDurationValue.Text = Config.NotificationDurationValue+" ms/c";
comboBoxIdlePause.Items.Add("Disabled");
comboBoxIdlePause.Items.Add("30 seconds");
comboBoxIdlePause.Items.Add("1 minute");
comboBoxIdlePause.Items.Add("2 minutes");
comboBoxIdlePause.Items.Add("5 minutes");
comboBoxIdlePause.SelectedIndex = Math.Max(0, Array.FindIndex(IdlePauseSeconds, val => val == Config.NotificationIdlePauseSeconds));
comboBoxDisplay.Items.Add("(Same as TweetDuck)"); comboBoxDisplay.Items.Add("(Same as TweetDuck)");
foreach(Screen screen in Screen.AllScreens){ foreach(Screen screen in Screen.AllScreens){
@@ -59,61 +78,57 @@ namespace TweetDuck.Core.Other.Settings{
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay); comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1, Config.NotificationDisplay);
checkColumnName.Checked = Config.DisplayNotificationColumn;
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
checkMediaPreviews.Checked = Config.NotificationMediaPreviews;
checkSkipOnLinkClick.Checked = Config.NotificationSkipOnLinkClick;
checkNonIntrusive.Checked = Config.NotificationNonIntrusiveMode;
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance); trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
this.notification.CanMoveWindow = () => radioLocCustom.Checked; switch(Config.NotificationSize){
this.notification.CanResizeWindow = radioSizeCustom.Checked; case TweetNotification.Size.Auto: radioSizeAuto.Checked = true; break;
case TweetNotification.Size.Custom: radioSizeCustom.Checked = true; break;
}
trackBarScrollSpeed.SetValueSafe(Config.NotificationScrollSpeed);
labelScrollSpeedValue.Text = trackBarScrollSpeed.Value+"%";
Disposed += (sender, args) => this.notification.Dispose(); Disposed += (sender, args) => this.notification.Dispose();
} }
public override void OnReady(){ public override void OnReady(){
radioLocTL.CheckedChanged += radioLoc_CheckedChanged; checkColumnName.CheckedChanged += checkColumnName_CheckedChanged;
radioLocTR.CheckedChanged += radioLoc_CheckedChanged; checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged;
radioLocBL.CheckedChanged += radioLoc_CheckedChanged; checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged;
radioLocBR.CheckedChanged += radioLoc_CheckedChanged; checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
radioLocCustom.Click += radioLocCustom_Click;
radioSizeAuto.CheckedChanged += radioSize_CheckedChanged; comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged;
radioSizeCustom.Click += radioSizeCustom_Click;
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged;
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged;
trackBarDuration.ValueChanged += trackBarDuration_ValueChanged; trackBarDuration.ValueChanged += trackBarDuration_ValueChanged;
btnDurationShort.Click += btnDurationShort_Click; btnDurationShort.Click += btnDurationShort_Click;
btnDurationMedium.Click += btnDurationMedium_Click; btnDurationMedium.Click += btnDurationMedium_Click;
btnDurationLong.Click += btnDurationLong_Click; btnDurationLong.Click += btnDurationLong_Click;
checkColumnName.CheckedChanged += checkColumnName_CheckedChanged; radioLocTL.CheckedChanged += radioLoc_CheckedChanged;
checkNotificationTimer.CheckedChanged += checkNotificationTimer_CheckedChanged; radioLocTR.CheckedChanged += radioLoc_CheckedChanged;
checkTimerCountDown.CheckedChanged += checkTimerCountDown_CheckedChanged; radioLocBL.CheckedChanged += radioLoc_CheckedChanged;
checkMediaPreviews.CheckedChanged += checkMediaPreviews_CheckedChanged; radioLocBR.CheckedChanged += radioLoc_CheckedChanged;
checkSkipOnLinkClick.CheckedChanged += checkSkipOnLinkClick_CheckedChanged; radioLocCustom.Click += radioLocCustom_Click;
checkNonIntrusive.CheckedChanged += checkNonIntrusive_CheckedChanged;
comboBoxIdlePause.SelectedValueChanged += comboBoxIdlePause_SelectedValueChanged;
trackBarScrollSpeed.ValueChanged += trackBarScrollSpeed_ValueChanged;
comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged; comboBoxDisplay.SelectedValueChanged += comboBoxDisplay_SelectedValueChanged;
trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_ValueChanged; trackBarEdgeDistance.ValueChanged += trackBarEdgeDistance_ValueChanged;
radioSizeAuto.CheckedChanged += radioSize_CheckedChanged;
radioSizeCustom.Click += radioSizeCustom_Click;
trackBarScrollSpeed.ValueChanged += trackBarScrollSpeed_ValueChanged;
} }
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){ private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
if (Parent == null){ if (Parent == null){
notification.HideNotification(false); notification.HideNotification();
} }
else{ else{
notification.ShowNotificationForSettings(true); notification.ShowExampleNotification(true);
} }
} }
@@ -131,7 +146,7 @@ namespace TweetDuck.Core.Other.Settings{
private void notification_ResizeEnd(object sender, EventArgs e){ private void notification_ResizeEnd(object sender, EventArgs e){
if (radioSizeCustom.Checked){ if (radioSizeCustom.Checked){
Config.CustomNotificationSize = notification.BrowserSize; Config.CustomNotificationSize = notification.BrowserSize;
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
} }
} }
@@ -142,7 +157,7 @@ namespace TweetDuck.Core.Other.Settings{
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight; else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = true;
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
} }
private void radioLocCustom_Click(object sender, EventArgs e){ private void radioLocCustom_Click(object sender, EventArgs e){
@@ -153,7 +168,7 @@ namespace TweetDuck.Core.Other.Settings{
Config.NotificationPosition = TweetNotification.Position.Custom; Config.NotificationPosition = TweetNotification.Position.Custom;
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false; comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = false;
notification.ShowNotificationForSettings(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;
@@ -167,10 +182,11 @@ namespace TweetDuck.Core.Other.Settings{
} }
private void radioSize_CheckedChanged(object sender, EventArgs e){ private void radioSize_CheckedChanged(object sender, EventArgs e){
if (radioSizeAuto.Checked)Config.NotificationSize = TweetNotification.Size.Auto; if (radioSizeAuto.Checked){
Config.NotificationSize = TweetNotification.Size.Auto;
}
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
notification.CanResizeWindow = false; // must be after ShowNotificationForSettings
} }
private void radioSizeCustom_Click(object sender, EventArgs e){ private void radioSizeCustom_Click(object sender, EventArgs e){
@@ -179,9 +195,7 @@ namespace TweetDuck.Core.Other.Settings{
} }
Config.NotificationSize = TweetNotification.Size.Custom; Config.NotificationSize = TweetNotification.Size.Custom;
notification.ShowExampleNotification(false);
notification.CanResizeWindow = true;
notification.ShowNotificationForSettings(false);
} }
private void trackBarDuration_ValueChanged(object sender, EventArgs e){ private void trackBarDuration_ValueChanged(object sender, EventArgs e){
@@ -206,18 +220,18 @@ namespace TweetDuck.Core.Other.Settings{
private void checkColumnName_CheckedChanged(object sender, EventArgs e){ private void checkColumnName_CheckedChanged(object sender, EventArgs e){
Config.DisplayNotificationColumn = checkColumnName.Checked; Config.DisplayNotificationColumn = checkColumnName.Checked;
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
} }
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){ private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
Config.DisplayNotificationTimer = checkNotificationTimer.Checked; Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked; checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
notification.ShowNotificationForSettings(true); notification.ShowExampleNotification(true);
} }
private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){ private void checkTimerCountDown_CheckedChanged(object sender, EventArgs e){
Config.NotificationTimerCountDown = checkTimerCountDown.Checked; Config.NotificationTimerCountDown = checkTimerCountDown.Checked;
notification.ShowNotificationForSettings(true); notification.ShowExampleNotification(true);
} }
private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){ private void checkMediaPreviews_CheckedChanged(object sender, EventArgs e){
@@ -245,17 +259,17 @@ namespace TweetDuck.Core.Other.Settings{
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){ private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex; Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
} }
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){ private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px"; labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value+" px";
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value; Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
notification.ShowNotificationForSettings(false); notification.ShowExampleNotification(false);
} }
private void durationUpdateTimer_Tick(object sender, EventArgs e){ private void durationUpdateTimer_Tick(object sender, EventArgs e){
notification.ShowNotificationForSettings(true); notification.ShowExampleNotification(true);
durationUpdateTimer.Stop(); durationUpdateTimer.Stop();
} }
} }

View File

@@ -34,8 +34,12 @@
this.panelSoundNotification = new System.Windows.Forms.Panel(); this.panelSoundNotification = new System.Windows.Forms.Panel();
this.labelVolume = new System.Windows.Forms.Label(); this.labelVolume = new System.Windows.Forms.Label();
this.trackBarVolume = new System.Windows.Forms.TrackBar(); this.trackBarVolume = new System.Windows.Forms.TrackBar();
this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.panelVolume = new System.Windows.Forms.Panel();
this.panelSoundNotification.SuspendLayout(); this.panelSoundNotification.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
this.flowPanel.SuspendLayout();
this.panelVolume.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// tbCustomSound // tbCustomSound
@@ -46,12 +50,11 @@
this.tbCustomSound.Name = "tbCustomSound"; this.tbCustomSound.Name = "tbCustomSound";
this.tbCustomSound.Size = new System.Drawing.Size(316, 20); this.tbCustomSound.Size = new System.Drawing.Size(316, 20);
this.tbCustomSound.TabIndex = 0; this.tbCustomSound.TabIndex = 0;
this.toolTip.SetToolTip(this.tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
// //
// labelVolumeValue // labelVolumeValue
// //
this.labelVolumeValue.BackColor = System.Drawing.Color.Transparent; this.labelVolumeValue.BackColor = System.Drawing.Color.Transparent;
this.labelVolumeValue.Location = new System.Drawing.Point(147, 84); this.labelVolumeValue.Location = new System.Drawing.Point(147, 4);
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);
@@ -98,8 +101,8 @@
// //
this.labelSoundNotification.AutoSize = true; this.labelSoundNotification.AutoSize = true;
this.labelSoundNotification.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelSoundNotification.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelSoundNotification.Location = new System.Drawing.Point(6, 8); this.labelSoundNotification.Location = new System.Drawing.Point(0, 0);
this.labelSoundNotification.Margin = new System.Windows.Forms.Padding(0, 2, 0, 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 = 1;
@@ -107,24 +110,21 @@
// //
// panelSoundNotification // panelSoundNotification
// //
this.panelSoundNotification.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.panelSoundNotification.Anchor = System.Windows.Forms.AnchorStyles.Top;
| System.Windows.Forms.AnchorStyles.Right)));
this.panelSoundNotification.Controls.Add(this.labelVolume);
this.panelSoundNotification.Controls.Add(this.trackBarVolume);
this.panelSoundNotification.Controls.Add(this.btnPlaySound); this.panelSoundNotification.Controls.Add(this.btnPlaySound);
this.panelSoundNotification.Controls.Add(this.tbCustomSound); this.panelSoundNotification.Controls.Add(this.tbCustomSound);
this.panelSoundNotification.Controls.Add(this.btnResetSound); this.panelSoundNotification.Controls.Add(this.btnResetSound);
this.panelSoundNotification.Controls.Add(this.btnBrowseSound); this.panelSoundNotification.Controls.Add(this.btnBrowseSound);
this.panelSoundNotification.Controls.Add(this.labelVolumeValue); this.panelSoundNotification.Location = new System.Drawing.Point(0, 20);
this.panelSoundNotification.Location = new System.Drawing.Point(9, 31); 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, 119); this.panelSoundNotification.Size = new System.Drawing.Size(322, 55);
this.panelSoundNotification.TabIndex = 2; this.panelSoundNotification.TabIndex = 2;
// //
// labelVolume // labelVolume
// //
this.labelVolume.AutoSize = true; this.labelVolume.AutoSize = true;
this.labelVolume.Location = new System.Drawing.Point(3, 67); this.labelVolume.Location = new System.Drawing.Point(3, 87);
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);
@@ -135,29 +135,55 @@
// //
this.trackBarVolume.AutoSize = false; this.trackBarVolume.AutoSize = false;
this.trackBarVolume.BackColor = System.Drawing.SystemColors.Control; this.trackBarVolume.BackColor = System.Drawing.SystemColors.Control;
this.trackBarVolume.Location = new System.Drawing.Point(3, 83); this.trackBarVolume.Location = new System.Drawing.Point(3, 3);
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.SmallChange = 1;
this.trackBarVolume.TabIndex = 5; this.trackBarVolume.TabIndex = 5;
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);
// //
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Controls.Add(this.labelSoundNotification);
this.flowPanel.Controls.Add(this.panelSoundNotification);
this.flowPanel.Controls.Add(this.labelVolume);
this.flowPanel.Controls.Add(this.panelVolume);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 136);
this.flowPanel.TabIndex = 3;
this.flowPanel.WrapContents = false;
//
// panelVolume
//
this.panelVolume.Controls.Add(this.trackBarVolume);
this.panelVolume.Controls.Add(this.labelVolumeValue);
this.panelVolume.Location = new System.Drawing.Point(0, 100);
this.panelVolume.Margin = new System.Windows.Forms.Padding(0);
this.panelVolume.Name = "panelVolume";
this.panelVolume.Size = new System.Drawing.Size(322, 36);
this.panelVolume.TabIndex = 2;
//
// TabSettingsSounds // TabSettingsSounds
// //
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.panelSoundNotification); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.labelSoundNotification);
this.Name = "TabSettingsSounds"; this.Name = "TabSettingsSounds";
this.Size = new System.Drawing.Size(340, 160); this.Size = new System.Drawing.Size(340, 154);
this.panelSoundNotification.ResumeLayout(false); this.panelSoundNotification.ResumeLayout(false);
this.panelSoundNotification.PerformLayout(); this.panelSoundNotification.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
this.flowPanel.ResumeLayout(false);
this.flowPanel.PerformLayout();
this.panelVolume.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
@@ -173,5 +199,7 @@
private System.Windows.Forms.Label labelVolume; private System.Windows.Forms.Label labelVolume;
private System.Windows.Forms.Label labelVolumeValue; private System.Windows.Forms.Label labelVolumeValue;
private System.Windows.Forms.TrackBar trackBarVolume; private System.Windows.Forms.TrackBar trackBarVolume;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
private System.Windows.Forms.Panel panelVolume;
} }
} }

View File

@@ -16,17 +16,18 @@ namespace TweetDuck.Core.Other.Settings{
soundNotification = new SoundNotification(); soundNotification = new SoundNotification();
soundNotification.PlaybackError += sound_PlaybackError; soundNotification.PlaybackError += sound_PlaybackError;
Disposed += (sender, args) => soundNotification.Dispose();
supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume); supportsChangingVolume = soundNotification.SetVolume(Config.NotificationSoundVolume);
toolTip.SetToolTip(tbCustomSound, "When empty, the default TweetDeck sound notification is used.");
trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath); trackBarVolume.Enabled = supportsChangingVolume && !string.IsNullOrEmpty(Config.NotificationSoundPath);
trackBarVolume.SetValueSafe(Config.NotificationSoundVolume); trackBarVolume.SetValueSafe(Config.NotificationSoundVolume);
labelVolumeValue.Text = trackBarVolume.Value+"%"; labelVolumeValue.Text = trackBarVolume.Value+"%";
tbCustomSound.Text = Config.NotificationSoundPath; tbCustomSound.Text = Config.NotificationSoundPath;
tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty); tbCustomSound_TextChanged(tbCustomSound, EventArgs.Empty);
Disposed += (sender, args) => soundNotification.Dispose();
} }
public override void OnReady(){ public override void OnReady(){

View File

@@ -24,54 +24,40 @@
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.panelTray = new System.Windows.Forms.Panel();
this.checkTrayHighlight = new System.Windows.Forms.CheckBox(); this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox(); this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayIcon = new System.Windows.Forms.Label(); this.labelTrayIcon = new System.Windows.Forms.Label();
this.labelTray = new System.Windows.Forms.Label(); this.labelTray = new System.Windows.Forms.Label();
this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.panelTray.SuspendLayout(); this.flowPanel = new System.Windows.Forms.FlowLayoutPanel();
this.flowPanel.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// panelTray
//
this.panelTray.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panelTray.Controls.Add(this.checkTrayHighlight);
this.panelTray.Controls.Add(this.comboBoxTrayType);
this.panelTray.Controls.Add(this.labelTrayIcon);
this.panelTray.Location = new System.Drawing.Point(9, 31);
this.panelTray.Name = "panelTray";
this.panelTray.Size = new System.Drawing.Size(322, 76);
this.panelTray.TabIndex = 1;
//
// checkTrayHighlight // checkTrayHighlight
// //
this.checkTrayHighlight.AutoSize = true; this.checkTrayHighlight.AutoSize = true;
this.checkTrayHighlight.Location = new System.Drawing.Point(6, 56); this.checkTrayHighlight.Location = new System.Drawing.Point(6, 77);
this.checkTrayHighlight.Margin = new System.Windows.Forms.Padding(6, 5, 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 = 2;
this.checkTrayHighlight.Text = "Enable Highlight"; this.checkTrayHighlight.Text = "Enable Highlight";
this.toolTip.SetToolTip(this.checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
this.checkTrayHighlight.UseVisualStyleBackColor = true; this.checkTrayHighlight.UseVisualStyleBackColor = true;
// //
// comboBoxTrayType // comboBoxTrayType
// //
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true; this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(5, 5); this.comboBoxTrayType.Location = new System.Drawing.Point(5, 25);
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 = 0;
this.toolTip.SetToolTip(this.comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
// //
// labelTrayIcon // labelTrayIcon
// //
this.labelTrayIcon.AutoSize = true; this.labelTrayIcon.AutoSize = true;
this.labelTrayIcon.Location = new System.Drawing.Point(3, 38); this.labelTrayIcon.Location = new System.Drawing.Point(3, 58);
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);
@@ -82,35 +68,48 @@
// //
this.labelTray.AutoSize = true; this.labelTray.AutoSize = true;
this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); this.labelTray.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelTray.Location = new System.Drawing.Point(6, 8); this.labelTray.Location = new System.Drawing.Point(0, 0);
this.labelTray.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); this.labelTray.Margin = new System.Windows.Forms.Padding(0);
this.labelTray.Name = "labelTray"; this.labelTray.Name = "labelTray";
this.labelTray.Size = new System.Drawing.Size(96, 20); this.labelTray.Size = new System.Drawing.Size(96, 20);
this.labelTray.TabIndex = 0; this.labelTray.TabIndex = 0;
this.labelTray.Text = "System Tray"; this.labelTray.Text = "System Tray";
// //
// flowPanel
//
this.flowPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowPanel.Controls.Add(this.labelTray);
this.flowPanel.Controls.Add(this.comboBoxTrayType);
this.flowPanel.Controls.Add(this.labelTrayIcon);
this.flowPanel.Controls.Add(this.checkTrayHighlight);
this.flowPanel.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
this.flowPanel.Location = new System.Drawing.Point(9, 9);
this.flowPanel.Name = "flowPanel";
this.flowPanel.Size = new System.Drawing.Size(322, 97);
this.flowPanel.TabIndex = 2;
this.flowPanel.WrapContents = false;
//
// TabSettingsTray // TabSettingsTray
// //
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.panelTray); this.Controls.Add(this.flowPanel);
this.Controls.Add(this.labelTray);
this.Name = "TabSettingsTray"; this.Name = "TabSettingsTray";
this.Size = new System.Drawing.Size(340, 119); this.Size = new System.Drawing.Size(340, 115);
this.panelTray.ResumeLayout(false); this.flowPanel.ResumeLayout(false);
this.panelTray.PerformLayout(); this.flowPanel.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.Panel panelTray;
private System.Windows.Forms.CheckBox checkTrayHighlight; private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.ComboBox comboBoxTrayType; private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayIcon; private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.Label labelTray; private System.Windows.Forms.Label labelTray;
private System.Windows.Forms.ToolTip toolTip; private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.FlowLayoutPanel flowPanel;
} }
} }

View File

@@ -5,6 +5,9 @@ namespace TweetDuck.Core.Other.Settings{
public TabSettingsTray(){ public TabSettingsTray(){
InitializeComponent(); InitializeComponent();
toolTip.SetToolTip(comboBoxTrayType, "Changes behavior of the Tray icon.\r\nRight-click the icon for an action menu.");
toolTip.SetToolTip(checkTrayHighlight, "Highlights the tray icon if there are new tweets.\r\nOnly works for columns with popup or audio notifications.\r\nThe icon resets when the main window is restored.");
comboBoxTrayType.Items.Add("Disabled"); comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only"); comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray"); comboBoxTrayType.Items.Add("Minimize to Tray");

View File

@@ -1,4 +1,4 @@
namespace TweetDuck.Core { namespace TweetDuck.Core.Other {
partial class TrayIcon { partial class TrayIcon {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.

View File

@@ -2,7 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDuck.Core{ namespace TweetDuck.Core.Other{
sealed partial class TrayIcon : Component{ sealed partial class TrayIcon : Component{
public enum Behavior{ // keep order public enum Behavior{ // keep order
Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined Disabled, DisplayOnly, MinimizeToTray, CloseToTray, Combined
@@ -12,7 +12,9 @@ namespace TweetDuck.Core{
public event EventHandler ClickClose; public event EventHandler ClickClose;
public bool Visible{ public bool Visible{
get => notifyIcon.Visible; get{
return notifyIcon.Visible;
}
set{ set{
if (value){ if (value){

222
Core/TweetDeckBrowser.cs Normal file
View File

@@ -0,0 +1,222 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetDuck.Updates;
namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{
public bool Ready { get; private set; }
public bool Enabled{
get => browser.Enabled;
set => browser.Enabled = value;
}
public bool IsTweetDeckWebsite{
get{
using(IFrame frame = browser.GetBrowser().MainFrame){
return TwitterUtils.IsTweetDeckWebsite(frame);
}
}
}
public event EventHandler PageLoaded;
private readonly ChromiumWebBrowser browser;
private readonly PluginManager plugins;
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge bridge){
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(owner),
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(owner),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser()
};
#if DEBUG
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
this.browser.FrameLoadStart += browser_FrameLoadStart;
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
this.browser.LoadError += browser_LoadError;
this.browser.RegisterAsyncJsObject("$TD", bridge);
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
this.browser.Dock = DockStyle.None;
this.browser.Location = ControlExtensions.InvisibleLocation;
owner.Controls.Add(browser);
this.plugins = plugins;
this.plugins.PluginChangedState += plugins_PluginChangedState;
Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
}
// setup and management
private void OnBrowserReady(){
if (!Ready){
browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill;
Ready = true;
}
}
public void Focus(){
browser.Focus();
}
public void Dispose(){
plugins.PluginChangedState -= plugins_PluginChangedState;
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
browser.Dispose();
}
// event handlers
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word);
}
browser.BeginInvoke(new Action(OnBrowserReady));
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
if (e.Frame.IsMain){
if (Program.UserConfig.ZoomLevel != 100){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
}
}
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties();
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js");
InjectBrowserCSS();
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
TweetDeckBridge.ResetStaticProperties();
if (Program.UserConfig.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
}
PageLoaded?.Invoke(this, EventArgs.Empty);
}
}
private void browser_LoadError(object sender, LoadErrorEventArgs e){
if (e.ErrorCode == CefErrorCode.Aborted){
return;
}
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
}
}
}
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
private void UserConfig_MuteToggled(object sender, EventArgs e){
UpdateProperties();
}
private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
// external handling
public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){
return new UpdateHandler(browser, settings);
}
public void HideVideoOverlay(bool focus){
if (focus){
browser.GetBrowser().GetHost().SendFocusEvent(true);
}
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
}
// javascript calls
public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
}
public void UpdateProperties(){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
}
public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
}
public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
}
public void OnMouseClickExtra(IntPtr param){
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
}
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
}
public void TriggerTweetScreenshot(){
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
}
public void ReloadColumns(){
browser.ExecuteScriptAsync("TDGF_reloadColumns()");
}
public void ApplyROT13(){
browser.ExecuteScriptAsync("TDGF_applyROT13()");
}
}
}

View File

@@ -1,48 +1,64 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Linq; using System.Linq;
using System.Threading;
namespace TweetDuck.Core.Utils{ namespace TweetDuck.Core.Utils{
static class BrowserCache{ static class BrowserCache{
private static bool ClearOnExit { get; set; } public static string CacheFolder => Path.Combine(Program.StoragePath, "Cache");
private static readonly string CacheFolder = Path.Combine(Program.StoragePath, "Cache"); private static bool ClearOnExit;
private static Timer AutoClearTimer;
private static IEnumerable<string> CacheFiles{ private static long CalculateCacheSize(){
get{ return new DirectoryInfo(CacheFolder).EnumerateFiles().Select(file => {
return Directory.EnumerateFiles(CacheFolder); try{
} return file.Length;
}catch{
return 0L;
}
}).Sum();
} }
public static void CalculateCacheSize(Action<long> callbackBytes){ public static void GetCacheSize(Action<Task<long>> callbackBytes){
Task<long> task = new Task<long>(() => { Task<long> task = new Task<long>(CalculateCacheSize);
return CacheFiles.Select(file => { task.ContinueWith(callbackBytes);
try{
return new FileInfo(file).Length;
}catch{
return 0L;
}
}).Sum();
});
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L), TaskContinuationOptions.ExecuteSynchronously);
task.Start(); task.Start();
} }
public static void RefreshTimer(){
bool shouldRun = Program.SystemConfig.ClearCacheAutomatically && !ClearOnExit;
if (!shouldRun && AutoClearTimer != null){
AutoClearTimer.Dispose();
AutoClearTimer = null;
}
else if (shouldRun && AutoClearTimer == null){
AutoClearTimer = new Timer(state => {
if (AutoClearTimer != null && CalculateCacheSize() >= Program.SystemConfig.ClearCacheThreshold*1024L*1024L){
SetClearOnExit();
}
}, null, TimeSpan.FromSeconds(30), TimeSpan.FromHours(4));
}
}
public static void SetClearOnExit(){ public static void SetClearOnExit(){
ClearOnExit = true; ClearOnExit = true;
RefreshTimer();
} }
public static void Exit(){ public static void Exit(){
if (AutoClearTimer != null){
AutoClearTimer.Dispose();
AutoClearTimer = null;
}
if (ClearOnExit){ if (ClearOnExit){
foreach(string file in CacheFiles){ try{
try{ Directory.Delete(CacheFolder, true);
File.Delete(file); }catch{
}catch{ // welp, too bad
// welp, too bad
}
} }
} }
} }

View File

@@ -12,7 +12,7 @@ namespace TweetDuck.Core.Utils{
static class BrowserUtils{ static class BrowserUtils{
public static string HeaderAcceptLanguage{ public static string HeaderAcceptLanguage{
get{ get{
string culture = Program.Culture.Name; string culture = Program.UserConfig.AppLocale;
if (culture == "en"){ if (culture == "en"){
return "en-us,en"; return "en-us,en";
@@ -31,7 +31,7 @@ namespace TweetDuck.Core.Utils{
args["disable-gpu-vsync"] = "1"; args["disable-gpu-vsync"] = "1";
} }
args["disable-extensions"] = "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";
@@ -70,12 +70,18 @@ namespace TweetDuck.Core.Utils{
switch(CheckUrl(url)){ switch(CheckUrl(url)){
case UrlCheckResult.Fine: case UrlCheckResult.Fine:
OpenExternalBrowserUnsafe(url); if (FormGuide.CheckGuideUrl(url, out string hash)){
FormGuide.Show(hash);
}
else{
WindowsUtils.OpenAssociatedProgram(url);
}
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 (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)){
OpenExternalBrowserUnsafe(url); WindowsUtils.OpenAssociatedProgram(url);
} }
break; break;
@@ -86,10 +92,6 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static void OpenExternalBrowserUnsafe(string url){
using(Process.Start(url)){}
}
public static string GetFileNameFromUrl(string url){ public static string GetFileNameFromUrl(string url){
string file = Path.GetFileName(new Uri(url).AbsolutePath); string file = Path.GetFileName(new Uri(url).AbsolutePath);
return string.IsNullOrEmpty(file) ? null : file; return string.IsNullOrEmpty(file) ? null : file;
@@ -99,9 +101,14 @@ namespace TweetDuck.Core.Utils{
return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty); return StringUtils.ConvertPascalCaseToScreamingSnakeCase(Enum.GetName(typeof(CefErrorCode), code) ?? string.Empty);
} }
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){ public static WebClient CreateWebClient(){
WebClient client = new WebClient{ Proxy = null }; WebClient client = new WebClient{ Proxy = null };
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent; client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
return client;
}
public static WebClient DownloadFileAsync(string url, string target, Action onSuccess, Action<Exception> onFailure){
WebClient client = CreateWebClient();
client.DownloadFileCompleted += (sender, args) => { client.DownloadFileCompleted += (sender, args) => {
if (args.Cancelled){ if (args.Cancelled){

52
Core/Utils/LocaleUtils.cs Normal file
View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace TweetDuck.Core.Utils{
static class LocaleUtils{
public static string LocaleFolder => Path.Combine(Program.ProgramPath, "locales");
public static IEnumerable<Item> ChromiumLocales => Directory
.EnumerateFiles(LocaleFolder, "*.pak", SearchOption.TopDirectoryOnly)
.Select(file => new Item(Path.GetFileNameWithoutExtension(file)))
.OrderBy(code => code);
// TD.languages.getSupportedTranslationDestinationLanguages() except for "ht", "in", "iw" which are missing/duplicates
public static IEnumerable<Item> TweetDeckTranslationLocales { get; } = new List<string>{
"bg", "ca", "zh-cn", "zh-tw", "cs", "da", "nl",
"en", "et", "fi", "fr", "de", "el", "he", "hi",
"hu", "id", "it", "ja", "ko", "lv", "lt", "no",
"pl", "pt", "ro", "ru", "sk", "sl", "es", "sv",
"th", "tr", "uk", "vi", "ar", "fa"
}.Select(code => new Item(code)).OrderBy(code => code).ToList();
public sealed class Item : IComparable<Item>{
public string Code { get; }
public CultureInfo Info { get; }
public Item(string code){
this.Code = code;
this.Info = CultureInfo.GetCultureInfo(code);
}
public override bool Equals(object obj){
return obj is Item other && Code.Equals(other.Code, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(){
return Code.GetHashCode();
}
public override string ToString(){
string capitalizedName = Info.TextInfo.ToTitleCase(Info.NativeName);
return Info.DisplayName == Info.NativeName ? capitalizedName : $"{capitalizedName}, {Info.DisplayName}";
}
public int CompareTo(Item other){
return string.Compare(Info.NativeName, other.Info.NativeName, false, CultureInfo.InvariantCulture);
}
}
}
}

View File

@@ -8,6 +8,7 @@ namespace TweetDuck.Core.Utils{
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")] [SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
static class NativeMethods{ static class NativeMethods{
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1); public static readonly IntPtr HOOK_HANDLED = new IntPtr(-1);
public const int HWND_TOPMOST = -1; public const int HWND_TOPMOST = -1;
@@ -51,6 +52,12 @@ namespace TweetDuck.Core.Utils{
[DllImport("user32.dll", EntryPoint = "SetWindowPos")] [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags); private static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint RegisterWindowMessage(string messageName);
[DllImport("user32.dll")] [DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info); private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
@@ -96,13 +103,18 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static void BroadcastMessage(uint msg, uint wParam, int lParam){
PostMessage(HWND_BROADCAST, msg, new UIntPtr(wParam), new IntPtr(lParam));
}
public static int GetMouseHookData(IntPtr ptr){ public static int GetMouseHookData(IntPtr ptr){
return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16; return Marshal.PtrToStructure<MSLLHOOKSTRUCT>(ptr).mouseData >> 16;
} }
public static int GetIdleSeconds(){ public static int GetIdleSeconds(){
LASTINPUTINFO info = new LASTINPUTINFO(); LASTINPUTINFO info = new LASTINPUTINFO{
info.cbSize = LASTINPUTINFO.Size; cbSize = LASTINPUTINFO.Size
};
if (!GetLastInputInfo(ref info)){ if (!GetLastInputInfo(ref info)){
return 0; return 0;

View File

@@ -18,5 +18,16 @@ namespace TweetDuck.Core.Utils{
public static string ConvertPascalCaseToScreamingSnakeCase(string str){ public static string ConvertPascalCaseToScreamingSnakeCase(string str){
return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper(); return Regex.Replace(str, @"(\p{Ll})(\P{Ll})|(\P{Ll})(\P{Ll}\p{Ll})", "$1$3_$2$4").ToUpper();
} }
public static int CountOccurrences(string source, string substring){
int count = 0, index = 0;
while((index = source.IndexOf(substring, index)) != -1){
index += substring.Length;
++count;
}
return count;
}
} }
} }

View File

@@ -21,6 +21,10 @@ namespace TweetDuck.Core.Utils{
"tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD" "tweetdeck", "TweetDeck", "tweetduck", "TweetDuck", "TD"
}; };
public static readonly string[] ValidImageExtensions = {
".jpg", ".jpeg", ".png", ".gif"
};
public enum ImageQuality{ public enum ImageQuality{
Default, Orig Default, Orig
} }
@@ -53,6 +57,10 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static string GetImageFileName(string url){
return BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(url));
}
public static void DownloadImage(string url, string username, ImageQuality quality){ public static void DownloadImage(string url, string username, ImageQuality quality){
DownloadImages(new string[]{ url }, username, quality); DownloadImages(new string[]{ url }, username, quality);
} }
@@ -65,7 +73,7 @@ namespace TweetDuck.Core.Utils{
string firstImageLink = GetMediaLink(urls[0], quality); string firstImageLink = GetMediaLink(urls[0], quality);
int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/')); int qualityIndex = firstImageLink.IndexOf(':', firstImageLink.LastIndexOf('/'));
string file = BrowserUtils.GetFileNameFromUrl(ExtractMediaBaseLink(firstImageLink)); string file = GetImageFileName(firstImageLink);
string ext = Path.GetExtension(file); // includes dot string ext = Path.GetExtension(file); // includes dot
string[] fileNameParts = qualityIndex == -1 ? new string[]{ string[] fileNameParts = qualityIndex == -1 ? new string[]{
@@ -103,7 +111,7 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static void DownloadVideo(string url){ public static void DownloadVideo(string url, string username){
string filename = BrowserUtils.GetFileNameFromUrl(url); string filename = BrowserUtils.GetFileNameFromUrl(url);
string ext = Path.GetExtension(filename); string ext = Path.GetExtension(filename);
@@ -111,7 +119,7 @@ namespace TweetDuck.Core.Utils{
AutoUpgradeEnabled = true, AutoUpgradeEnabled = true,
OverwritePrompt = true, OverwritePrompt = true,
Title = "Save video", Title = "Save video",
FileName = filename, FileName = string.IsNullOrEmpty(username) ? filename : $"{username} {filename}",
Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}") Filter = "Video"+(string.IsNullOrEmpty(ext) ? " (unknown)|*.*" : $" (*{ext})|*{ext}")
}){ }){
if (dialog.ShowDialog() == DialogResult.OK){ if (dialog.ShowDialog() == DialogResult.OK){

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Management;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@@ -49,17 +49,22 @@ namespace TweetDuck.Core.Utils{
} }
} }
public static Process StartProcess(string file, string arguments, bool runElevated){ public static bool OpenAssociatedProgram(string file, string arguments = "", bool runElevated = false){
ProcessStartInfo processInfo = new ProcessStartInfo{ try{
FileName = file, using(Process.Start(new ProcessStartInfo{
Arguments = arguments FileName = file,
}; Arguments = arguments,
Verb = runElevated ? "runas" : string.Empty,
if (runElevated){ ErrorDialog = true
processInfo.Verb = "runas"; })){
return true;
}
}catch(Win32Exception e) when (e.NativeErrorCode == 0x000004C7){ // operation canceled by the user
return false;
}catch(Exception e){
Program.Reporter.HandleException("Error opening file", e.Message, true, e);
return false;
} }
return Process.Start(processInfo);
} }
public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){ public static bool TrySleepUntil(Func<bool> test, int timeoutMillis, int timeStepMillis){
@@ -89,22 +94,8 @@ namespace TweetDuck.Core.Utils{
}).Start(); }).Start();
} }
public static bool IsChildProcess(int pid){
try{
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = "+pid)){
foreach(ManagementBaseObject obj in searcher.Get()){
return (uint)obj["ParentProcessId"] == CurrentProcessID;
}
}
return false;
}catch{
return false;
}
}
public static void ClipboardStripHtmlStyles(){ public static void ClipboardStripHtmlStyles(){
if (!Clipboard.ContainsText(TextDataFormat.Html)){ if (!Clipboard.ContainsText(TextDataFormat.Html) || !Clipboard.ContainsText(TextDataFormat.UnicodeText)){
return; return;
} }

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 TweetDuck.Core.Utils;
namespace TweetDuck.Data.Serialization{ namespace TweetDuck.Data.Serialization{
sealed class FileSerializer<T>{ sealed class FileSerializer<T>{
@@ -28,6 +29,8 @@ namespace TweetDuck.Data.Serialization{
} }
public void Write(string file, T obj){ public void Write(string file, T obj){
WindowsUtils.CreateDirectoryForFile(file);
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){ using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))){
foreach(KeyValuePair<string, PropertyInfo> prop in props){ foreach(KeyValuePair<string, PropertyInfo> prop in props){
Type type = prop.Value.PropertyType; Type type = prop.Value.PropertyType;
@@ -54,8 +57,12 @@ namespace TweetDuck.Data.Serialization{
Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4); Dictionary<string, string> unknownProperties = new Dictionary<string, string>(4);
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))){
if (reader.Peek() <= 1){ switch(reader.Peek()){
throw new FormatException("Input appears to be a binary file."); case -1:
throw new FormatException("File is empty.");
case 0:
case 1:
throw new FormatException("Input appears to be a binary file.");
} }
foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){ foreach(string line in reader.ReadToEnd().Split(new string[]{ NewLineReal }, StringSplitOptions.RemoveEmptyEntries)){
@@ -95,6 +102,21 @@ namespace TweetDuck.Data.Serialization{
} }
} }
public void ReadIfExists(string file, T obj){
try{
Read(file, obj);
}catch(FileNotFoundException){
}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

@@ -62,9 +62,7 @@ namespace TweetDuck.Plugins.Controls{
} }
private void btnToggleState_Click(object sender, EventArgs e){ private void btnToggleState_Click(object sender, EventArgs e){
bool newState = !pluginManager.Config.IsEnabled(plugin); pluginManager.Config.ToggleEnabled(plugin);
pluginManager.Config.SetEnabled(plugin, newState);
UpdatePluginState(); UpdatePluginState();
} }

View File

@@ -7,12 +7,11 @@ using TweetDuck.Plugins.Enums;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
sealed class Plugin{ sealed class Plugin{
private static readonly Version AppVersion = new Version(Program.VersionTag);
private const string VersionWildcard = "*"; private const string VersionWildcard = "*";
public string Identifier { get; } public string Identifier { get; }
public PluginGroup Group { get; } public PluginGroup Group { get; }
public PluginEnvironment Environments { get; private set; } public PluginEnvironment Environments { get; }
public string Name => metadata["NAME"]; public string Name => metadata["NAME"];
public string Description => metadata["DESCRIPTION"]; public string Description => metadata["DESCRIPTION"];
@@ -23,9 +22,7 @@ namespace TweetDuck.Plugins{
public string ConfigDefault => metadata["CONFIGDEFAULT"]; public string ConfigDefault => metadata["CONFIGDEFAULT"];
public string RequiredVersion => metadata["REQUIRES"]; public string RequiredVersion => metadata["REQUIRES"];
public bool CanRun{ public bool CanRun { get; private set; }
get => canRun ?? (canRun = CheckRequiredVersion(RequiredVersion)).Value;
}
public bool HasConfig{ public bool HasConfig{
get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0; get => ConfigFile.Length > 0 && GetFullPathIfSafe(PluginFolder.Data, ConfigFile).Length > 0;
@@ -56,21 +53,18 @@ namespace TweetDuck.Plugins{
{ "REQUIRES", VersionWildcard } { "REQUIRES", VersionWildcard }
}; };
private bool? canRun; private Plugin(string path, string name, PluginGroup group, PluginEnvironment environments){
private Plugin(string path, PluginGroup group){
string name = Path.GetFileName(path);
System.Diagnostics.Debug.Assert(name != null);
this.pathRoot = path; this.pathRoot = path;
this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name); this.pathData = Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name);
this.Identifier = group.GetIdentifierPrefix()+name; this.Identifier = group.GetIdentifierPrefix()+name;
this.Group = group; this.Group = group;
this.Environments = PluginEnvironment.None; this.Environments = environments;
} }
private void OnMetadataLoaded(){ private void OnMetadataLoaded(){
CanRun = CheckRequiredVersion(RequiredVersion);
string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath; string configPath = ConfigPath, defaultConfigPath = DefaultConfigPath;
if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){ if (configPath.Length > 0 && defaultConfigPath.Length > 0 && !File.Exists(configPath) && File.Exists(defaultConfigPath)){
@@ -80,7 +74,7 @@ namespace TweetDuck.Plugins{
Directory.CreateDirectory(dataFolder); Directory.CreateDirectory(dataFolder);
File.Copy(defaultConfigPath, configPath, false); File.Copy(defaultConfigPath, configPath, false);
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Plugin Loading Error", "Could not generate a configuration file for '"+Identifier+"' plugin.", true, e); throw new IOException("Could not generate a configuration file for '"+Identifier+"' plugin: "+e.Message, e);
} }
} }
} }
@@ -138,89 +132,74 @@ namespace TweetDuck.Plugins{
return obj is Plugin plugin && plugin.Identifier.Equals(Identifier); return obj is Plugin plugin && plugin.Identifier.Equals(Identifier);
} }
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){ // Static
Plugin plugin = new Plugin(path, group);
if (!LoadMetadata(path, plugin, out error)){ private static readonly Version AppVersion = new Version(Program.VersionTag);
return null; private static readonly string[] EndTag = { "[END]" };
}
if (!LoadEnvironments(path, plugin, out error)){ public static Plugin CreateFromFolder(string path, PluginGroup group){
return null; Plugin plugin = new Plugin(path, Path.GetFileName(path), group, LoadEnvironments(path));
} LoadMetadata(path, plugin);
error = string.Empty;
return plugin; return plugin;
} }
private static bool LoadEnvironments(string path, Plugin plugin, out string error){ private static PluginEnvironment LoadEnvironments(string path){
PluginEnvironment environments = PluginEnvironment.None;
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){ foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
plugin.Environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)); environments |= PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
} }
if (plugin.Environments == PluginEnvironment.None){ if (environments == PluginEnvironment.None){
error = "Plugin has no script files."; throw new ArgumentException("Plugin has no script files");
return false;
} }
error = string.Empty; return environments;
return true;
} }
private static readonly string[] endTag = { "[END]" }; private static void LoadMetadata(string path, Plugin plugin){
private static bool LoadMetadata(string path, Plugin plugin, out string error){
string metaFile = Path.Combine(path, ".meta"); string metaFile = Path.Combine(path, ".meta");
if (!File.Exists(metaFile)){ if (!File.Exists(metaFile)){
error = "Missing .meta file."; throw new ArgumentException("Missing .meta file");
return false;
} }
string[] lines = File.ReadAllLines(metaFile, Encoding.UTF8); string currentTag = null, currentContents = string.Empty;
string currentTag = null, currentContents = "";
foreach(string line in lines.Concat(endTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){ foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
if (line[0] == '[' && line[line.Length-1] == ']'){ if (line[0] == '[' && line[line.Length-1] == ']'){
if (currentTag != null){ if (currentTag != null){
plugin.metadata[currentTag] = currentContents; plugin.metadata[currentTag] = currentContents;
} }
currentTag = line.Substring(1, line.Length-2).ToUpper(); currentTag = line.Substring(1, line.Length-2).ToUpper();
currentContents = ""; currentContents = string.Empty;
if (line.Equals(endTag[0])){ if (line.Equals(EndTag[0])){
break; break;
} }
if (!plugin.metadata.ContainsKey(currentTag)){ if (!plugin.metadata.ContainsKey(currentTag)){
error = "Invalid metadata tag: "+currentTag; throw new FormatException("Invalid metadata tag: "+currentTag);
return false;
} }
} }
else if (currentTag != null){ else if (currentTag != null){
currentContents = currentContents.Length == 0 ? line : currentContents+"\r\n"+line; currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
} }
else{ else{
error = "Missing metadata tag before value: "+line; throw new FormatException("Missing metadata tag before value: "+line);
return false;
} }
} }
if (plugin.Name.Length == 0){ if (plugin.Name.Length == 0){
error = "Plugin is missing a name in the .meta file."; throw new FormatException("Plugin is missing a name in the .meta file");
return false;
} }
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){ if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion == VersionWildcard || System.Version.TryParse(plugin.RequiredVersion, out Version _))){
error = "Plugin contains invalid version: "+plugin.RequiredVersion; throw new FormatException("Plugin contains invalid version: "+plugin.RequiredVersion);
return false;
} }
plugin.OnMetadataLoaded(); plugin.OnMetadataLoaded();
error = string.Empty;
return true;
} }
private static bool CheckRequiredVersion(string requires){ private static bool CheckRequiredVersion(string requires){

View File

@@ -24,10 +24,28 @@ namespace TweetDuck.Plugins{
} }
} }
public void ToggleEnabled(Plugin plugin){
SetEnabled(plugin, !IsEnabled(plugin));
}
public bool IsEnabled(Plugin plugin){ public bool IsEnabled(Plugin plugin){
return !disabled.Contains(plugin.Identifier); return !disabled.Contains(plugin.Identifier);
} }
public void Save(string file){
try{
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
writer.WriteLine("#Disabled");
foreach(string identifier in disabled){
writer.WriteLine(identifier);
}
}
}catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
}
}
public void Load(string file){ public void Load(string file){
try{ try{
using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){ using(StreamReader reader = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8)){
@@ -43,30 +61,12 @@ namespace TweetDuck.Plugins{
} }
}catch(FileNotFoundException){ }catch(FileNotFoundException){
disabled.Clear(); disabled.Clear();
disabled.UnionWith(DefaultDisabled);
foreach(string identifier in DefaultDisabled){
disabled.Add(identifier);
}
Save(file); Save(file);
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
}catch(Exception e){ }catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e); Program.Reporter.HandleException("Plugin Configuration Error", "Could not read the plugin configuration file. If you continue, the list of disabled plugins will be reset to default.", true, e);
} }
} }
public void Save(string file){
try{
using(StreamWriter writer = new StreamWriter(new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None), Encoding.UTF8)){
writer.WriteLine("#Disabled");
foreach(string identifier in disabled){
writer.WriteLine(identifier);
}
}
}catch(Exception e){
Program.Reporter.HandleException("Plugin Configuration Error", "Could not save the plugin configuration file.", true, e);
}
}
} }
} }

View File

@@ -9,8 +9,6 @@ using TweetDuck.Resources;
namespace TweetDuck.Plugins{ namespace TweetDuck.Plugins{
sealed class PluginManager{ sealed class PluginManager{
private const int InvalidToken = 0;
private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){ private static readonly Dictionary<PluginEnvironment, string> PluginSetupScripts = new Dictionary<PluginEnvironment, string>(4){
{ PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") }, { PluginEnvironment.None, ScriptLoader.LoadResource("plugins.js") },
{ PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") }, { PluginEnvironment.Browser, ScriptLoader.LoadResource("plugins.browser.js") },
@@ -35,8 +33,6 @@ 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();
private List<string> loadErrors;
public PluginManager(string rootPath, string configPath){ public PluginManager(string rootPath, string configPath){
this.rootPath = rootPath; this.rootPath = rootPath;
this.configPath = configPath; this.configPath = configPath;
@@ -68,7 +64,18 @@ namespace TweetDuck.Plugins{
} }
} }
return InvalidToken; int token, attempts = 1000;
do{
token = rand.Next();
}while(tokens.ContainsKey(token) && --attempts >= 0);
if (attempts < 0){
token = -tokens.Count-1;
}
tokens[token] = plugin;
return token;
} }
public Plugin GetPluginFromToken(int token){ public Plugin GetPluginFromToken(int token){
@@ -81,28 +88,41 @@ namespace TweetDuck.Plugins{
plugins.Clear(); plugins.Clear();
tokens.Clear(); tokens.Clear();
loadErrors = new List<string>(2); List<string> loadErrors = new List<string>(2);
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official)){ IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
plugins.Add(plugin); if (!Directory.Exists(path)){
yield break;
}
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
Plugin plugin;
try{
plugin = Plugin.CreateFromFolder(fullDir, group);
}catch(Exception e){
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
continue;
}
yield return plugin;
}
} }
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom)){ plugins.UnionWith(LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official));
plugins.Add(plugin); plugins.UnionWith(LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom));
}
Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors)); Reloaded?.Invoke(this, new PluginErrorEventArgs(loadErrors));
} }
public void ExecutePlugins(IFrame frame, PluginEnvironment environment){ public void ExecutePlugins(IFrame frame, PluginEnvironment environment){
if (HasAnyPlugin(environment)){ if (!HasAnyPlugin(environment)){
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier()); return;
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
ExecutePluginScripts(frame, environment);
} }
}
private void ExecutePluginScripts(IFrame frame, PluginEnvironment environment){ ScriptLoader.ExecuteScript(frame, PluginSetupScripts[environment], environment.GetScriptIdentifier());
ScriptLoader.ExecuteScript(frame, PluginSetupScripts[PluginEnvironment.None], PluginEnvironment.None.GetScriptIdentifier());
bool includeDisabled = environment.IncludesDisabledPlugins(); bool includeDisabled = environment.IncludesDisabledPlugins();
if (includeDisabled){ if (includeDisabled){
@@ -113,7 +133,10 @@ namespace TweetDuck.Plugins{
foreach(Plugin plugin in Plugins){ foreach(Plugin plugin in Plugins){
string path = plugin.GetScriptPath(environment); string path = plugin.GetScriptPath(environment);
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun)continue;
if (string.IsNullOrEmpty(path) || (!includeDisabled && !Config.IsEnabled(plugin)) || !plugin.CanRun){
continue;
}
string script; string script;
@@ -124,49 +147,10 @@ namespace TweetDuck.Plugins{
continue; continue;
} }
int token; ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, GetTokenFromPlugin(plugin), environment), "plugin:"+plugin);
if (tokens.ContainsValue(plugin)){
token = GetTokenFromPlugin(plugin);
}
else{
token = GenerateToken();
tokens[token] = plugin;
}
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, token, environment), "plugin:"+plugin);
} }
Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins)); Executed?.Invoke(this, new PluginErrorEventArgs(failedPlugins));
} }
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
if (!Directory.Exists(path)){
yield break;
}
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
Plugin plugin = Plugin.CreateFromFolder(fullDir, group, out string error);
if (plugin == null){
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
}
else{
yield return plugin;
}
}
}
private int GenerateToken(){
for(int attempt = 0; attempt < 1000; attempt++){
int token = rand.Next();
if (!tokens.ContainsKey(token) && token != InvalidToken){
return token;
}
}
return -tokens.Count;
}
} }
} }

View File

@@ -14,14 +14,13 @@ using TweetDuck.Core.Other.Settings.Export;
using TweetDuck.Core.Utils; using TweetDuck.Core.Utils;
using TweetDuck.Data; using TweetDuck.Data;
using TweetDuck.Updates; using TweetDuck.Updates;
using TweetLib.Communication;
namespace TweetDuck{ namespace TweetDuck{
static class Program{ static class Program{
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.10"; public const string VersionTag = "1.11.2";
public static readonly bool IsPortable = File.Exists("makeportable"); public static readonly bool IsPortable = File.Exists("makeportable");
@@ -37,12 +36,12 @@ namespace TweetDuck{
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");
public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg"); public static string PluginConfigFilePath => Path.Combine(StoragePath, "TD_PluginConfig.cfg");
public static string AnalyticsFilePath => Path.Combine(StoragePath, "TD_Analytics.cfg");
private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt"); private static string ErrorLogFilePath => Path.Combine(StoragePath, "TD_Log.txt");
private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt"); private static string ConsoleLogFilePath => Path.Combine(StoragePath, "TD_Console.txt");
public static uint WindowRestoreMessage; public static uint WindowRestoreMessage;
public static uint SubProcessMessage;
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock")); private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath, ".lock"));
private static bool HasCleanedUp; private static bool HasCleanedUp;
@@ -69,9 +68,9 @@ namespace TweetDuck{
private static void Main(){ private static void Main(){
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Cef.EnableHighDPISupport();
WindowRestoreMessage = Comms.RegisterMessage("TweetDuckRestore"); WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
SubProcessMessage = Comms.RegisterMessage("TweetDuckSubProcess");
if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){ if (!WindowsUtils.CheckFolderWritePermission(StoragePath)){
FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK); FormMessage.Warning("Permission Error", "TweetDuck does not have write permissions to the storage folder: "+StoragePath, FormMessage.OK);
@@ -128,12 +127,13 @@ namespace TweetDuck{
WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000); WindowsUtils.TryDeleteFolderWhenAble(InstallerPath, 8000);
} }
BrowserCache.RefreshTimer();
CefSharpSettings.WcfEnabled = false; CefSharpSettings.WcfEnabled = false;
CefSettings settings = new CefSettings{ CefSettings settings = new CefSettings{
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage, AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
UserAgent = BrowserUtils.HeaderUserAgent, UserAgent = BrowserUtils.HeaderUserAgent,
Locale = Arguments.GetValue(Arguments.ArgLocale, string.Empty), Locale = UserConfig.AppLocale,
BrowserSubprocessPath = BrandName+".Browser.exe", BrowserSubprocessPath = BrandName+".Browser.exe",
CachePath = StoragePath, CachePath = StoragePath,
LogFile = ConsoleLogFilePath, LogFile = ConsoleLogFilePath,
@@ -145,7 +145,6 @@ namespace TweetDuck{
CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs); CommandLineArgs.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs); BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
Cef.EnableHighDPISupport();
Cef.Initialize(settings, false, new BrowserProcessHandler()); Cef.Initialize(settings, false, new BrowserProcessHandler());
Application.ApplicationExit += (sender, args) => ExitCleanup(); Application.ApplicationExit += (sender, args) => ExitCleanup();
@@ -166,8 +165,12 @@ namespace TweetDuck{
string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : ""); string updaterArgs = "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\" /RUNARGS=\""+Arguments.GetCurrentForInstallerCmd()+"\""+(IsPortable ? " /PORTABLE=1" : "");
bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath); bool runElevated = !IsPortable || !WindowsUtils.CheckFolderWritePermission(ProgramPath);
WindowsUtils.StartProcess(mainForm.UpdateInstallerPath, updaterArgs, runElevated); if (WindowsUtils.OpenAssociatedProgram(mainForm.UpdateInstallerPath, updaterArgs, runElevated)){
Application.Exit(); Application.Exit();
}
else{
RestartWithArgsInternal(Arguments.GetCurrentClean());
}
} }
} }
@@ -211,11 +214,14 @@ namespace TweetDuck{
FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault(); FormBrowser browserForm = Application.OpenForms.OfType<FormBrowser>().FirstOrDefault();
if (browserForm == null)return; if (browserForm == null)return;
args.AddFlag(Arguments.ArgRestart);
browserForm.ForceClose(); browserForm.ForceClose();
ExitCleanup();
ExitCleanup();
RestartWithArgsInternal(args);
}
private static void RestartWithArgsInternal(CommandLineArgs args){
args.AddFlag(Arguments.ArgRestart);
Process.Start(Application.ExecutablePath, args.ToString()); Process.Start(Application.ExecutablePath, args.ToString());
Application.Exit(); Application.Exit();
} }

View File

@@ -1,3 +1,7 @@
# 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)
# Build Instructions # Build Instructions
### Setup ### Setup
@@ -10,10 +14,12 @@ The program was built using Visual Studio 2017. Before opening the solution, ple
After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**: After opening the solution, download the following NuGet packages by right-clicking on the solution and selecting **Restore NuGet Packages**, or manually running these commands in the **Package Manager Console**:
``` ```
PM> Install-Package CefSharp.WinForms -Version 57.0.0 PM> Install-Package CefSharp.WinForms -Version 63.0.0-pre01 -Source https://www.myget.org/F/cefsharp/api/v3/index.json
PM> Install-Package Microsoft.VC120.CRT.JetBrains PM> Install-Package Microsoft.VC120.CRT.JetBrains
``` ```
Note that some pre-release builds of CefSharp are not available on NuGet. To correctly restore packages in that case, open **Package Manager Settings**, and add `https://www.myget.org/F/cefsharp/api/v3/index.json` to the list of package sources.
### Debug ### Debug
It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution. It is recommended to create a separate data folder for debugging, otherwise you will not be able to run TweetDuck while debugging the solution.
@@ -25,21 +31,34 @@ To do that, open **TweetDuck Properties**, click the **Debug** tab, make sure yo
### Build ### Build
To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, open the **Output** view and see which part of the build failed. To make a release build of TweetDuck, open **Batch Build**, tick all `Release` configurations except for the `UnitTest` project (otherwise the build will fail), and click **Rebuild**. Check the status bar to make sure it says **Rebuild All succeeded**; if not, see the [Troubleshooting](#troubleshooting) section.
After the build succeeds, the **bin/x86/Release** folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#Installers) section for automated installer generation. After the build succeeds, the `bin/x86/Release` folder will contain files intended for distribution (no debug symbols or other unnecessary files). You may package these files yourself, or see the [Installers](#installers) section for automated installer generation.
If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck. If you decide to release a custom version publicly, please make it clear that it is not an official release of TweetDuck.
### Troubleshooting
There are a few quirks in the build process that may catch you off guard:
- **Plugin files are not updated automatically**
- Since official plugins (`Resources/Plugins`) are not included in the project, Visual Studio will not automatically detect changes in the files
- To ensure plugins are updated when testing the app, click **Rebuild Solution** before clicking **Start**
- **Error: The command (...) exited with code 1**
- If the post-build event fails, open the **Output** tab and look for the cause
- Determine if there was an IO error while copying files or modifying folders, or whether the final .ps1 script failed (`Encountered an error while running PostBuild.ps1 on line xyz`)
- Some files are checked for invalid characters:
- `Resources/Plugins/emoji-keyboard/emoji-ordering.txt` line endings must be LF (line feed); any CR (carriage return) in the file will cause a failed build, and you will need to ensure correct line endings in your text editor
### Installers ### Installers
TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin). TweetDuck uses **Inno Setup** to automate the creation of installers. First, download and install [InnoSetup QuickStart Pack](http://www.jrsoftware.org/isdl.php) (non-unicode; editor and encryption support not required) and the [Inno Download Plugin](https://code.google.com/archive/p/inno-download-plugin).
Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place. Next, add the Inno Setup installation folder (usually `C:\Program Files (x86)\Inno Setup 5`) into your **PATH** environment variable. You may need to restart File Explorer for the change to take place.
Now you can generate installers after a build by running **bld/RUN BUILD.bat**. Note that despite the name, this will only package the files, you still need to run the [build](#Build) in Visual Studio! Now you can generate installers after a build by running `bld/RUN BUILD.bat`. Note that despite the name, this will only package the files, you still need to run the [build](#build) in Visual Studio!
After the window closes, three installers will be generated inside the **bld/Output** folder: After the window closes, three installers will be generated inside the `bld/Output` folder:
* **TweetDuck.exe** * **TweetDuck.exe**
* This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon * This is the main installer that creates entries in the Start Menu & Programs and Features, and an optional desktop icon
* **TweetDuck.Update.exe** * **TweetDuck.Update.exe**

View File

@@ -28,7 +28,7 @@
<img src="img/app-menu.png" alt=""> <img src="img/app-menu.png" alt="">
<p>There are two ways to open the main menu:</p> <p>There are two ways to open the main menu:</p>
<ul> <ul>
<li>Click <em>Settings</em> in the left panel, then select <em>TweetDuck</em></li> <li>Click <em>Settings</em> in the left panel and select <em>TweetDuck</em>, or...</li>
<li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li> <li><em>Right-click anywhere</em> and you will either see the listed options, or a <em>TweetDuck</em> entry that contains these options</li>
</ul> </ul>
<img src="img/settings-dropdown.png" alt=""> <img src="img/settings-dropdown.png" alt="">
@@ -57,9 +57,10 @@
<details> <details>
<summary id="emoji">How to add emoji to tweets</summary> <summary id="emoji">How to add emoji to tweets</summary>
<div> <div>
<p>When writing a new tweet, click the heart icon to open an emoji picker. If you're writing a reply, click the <em>Popout</em> icon first to bring the reply into the large panel.</p> <p>When writing a new tweet, click the heart icon to open an emoji picker. If you're writing a reply, click the <em>Popout</em> icon to bring the reply into the New Tweet panel.</p>
<p>Then you can immediately type into the <em>Search</em> field, which accepts keywords separated by space. Pressing <em>Enter</em> in the search field (when not empty) will insert the first result into your tweet. Pressing <em>Escape</em> closes the emoji picker.</p> <p>Then you can immediately type into the <em>Search</em> field, which accepts keywords separated by space. Pressing <em>Enter</em> in the search field (when not empty) will insert the first result into your tweet. Pressing <em>Escape</em> closes the emoji picker.</p>
<p>You can also use your mouse to scroll through the emoji, click on the emoji to insert them, and change the skin tone.</p> <p>You can also use your mouse to change the skin tone, scroll through the emoji, and click on the emoji to insert them.</p>
<p>Typing <em>:emoji_name:</em> in the New Tweet panel will automatically search for an emoji using those keywords. If a single emoji is found, it will be inserted into the tweet (press <em>Backspace</em> or <em>Escape</em> to revert it). If multiple emojis are found, it will open the emoji picker where you can refine the search, or press <em>Escape</em> to go back.</p>
<p>Emoji are provided by an official plugin called <em>Emoji keyboard</em>, which is enabled by default. The heart icon will not show if the plugin is disabled.</p> <p>Emoji are provided by an official plugin called <em>Emoji keyboard</em>, which is enabled by default. The heart icon will not show if the plugin is disabled.</p>
<img src="img/new-tweet-emoji.png" alt=""> <img src="img/new-tweet-emoji.png" alt="">
</div> </div>
@@ -124,7 +125,7 @@
<li><em>Save image/video as...</em></li> <li><em>Save image/video as...</em></li>
</ul> </ul>
<p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p> <p>TweetDuck will attempt to fetch the highest quality image when you click any of these options. You can disable that by going to the <em>main menu</em>, selecting <em>Options</em>, and then unchecking <em>Best Image Quality</em>.</p>
<p>Whenever possible, the username and quality are included in the filename by default, for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p> <p>Whenever possible, the username and quality are included in the filename by default for convenience. After you select a folder and click Save, the image/video will be downloaded in the background. There is no notification for when the download finishes, but you will be notified if it fails.</p>
</div> </div>
</details> </details>
@@ -330,10 +331,10 @@
</details> </details>
<details> <details>
<summary id="popout-replies">How to instantly popout replies</summary> <summary id="popout-replies">How to instantly popout replies or quote tweets</summary>
<div> <div>
<p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel.</p> <p><em>Middle-click the reply icon</em> to instantly open your reply in the New Tweet panel, or <em>middle-click the retweet icon</em> to quote a tweet instead of retweeting it.</p>
<p>Middle-clicks are usually done by pressing your mouse wheel as if it was a button. When using a laptop touchpad or certain mice, the ways of triggering a middle click vary.</p> <p>Middle-clicking is usually done by pressing your mouse wheel as if it was a button. Some mice or other devices, such as laptop touchpads, may have a dedicated button or button combination instead.</p>
</div> </div>
</details> </details>

View File

@@ -71,7 +71,17 @@ var init = function(){
if (element && element.tagName === "SUMMARY"){ if (element && element.tagName === "SUMMARY"){
element.click(); element.click();
element.blur();
element.scrollIntoView(true); element.scrollIntoView(true);
if (window.innerWidth === 0){
var ffs = function(){
element.scrollIntoView(true);
window.removeEventListener("resize", ffs);
};
window.addEventListener("resize", ffs);
}
} }
} }
}; };

View File

@@ -8,10 +8,10 @@ Edit layout & design
chylex chylex
[version] [version]
1.1.3 1.1.8
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com
[requires] [requires]
1.7 1.10.2

View File

@@ -6,12 +6,15 @@ enabled(){
this.config = null; this.config = null;
this.defaultConfig = { this.defaultConfig = {
_theme: "light",
columnWidth: "310px", columnWidth: "310px",
fontSize: "12px", fontSize: "12px",
hideTweetActions: true, hideTweetActions: true,
moveTweetActionsToRight: true, moveTweetActionsToRight: true,
themeColorTweaks: true, themeColorTweaks: true,
revertIcons: true, revertIcons: true,
showCharacterCount: true,
increaseQuoteTextSize: false,
smallComposeTextSize: false, smallComposeTextSize: false,
optimizeAnimations: true, optimizeAnimations: true,
avatarRadius: 2 avatarRadius: 2
@@ -37,11 +40,13 @@ enabled(){
this.currentStage = 1; this.currentStage = 1;
} }
else if (this.tmpConfig !== null){ else if (this.tmpConfig !== null){
let needsResave = !("_theme" in this.tmpConfig);
this.config = $.extend(this.defaultConfig, this.tmpConfig); this.config = $.extend(this.defaultConfig, this.tmpConfig);
this.tmpConfig = null; this.tmpConfig = null;
this.reinjectAll(); this.reinjectAll();
if (this.firstTimeLoad){ if (this.firstTimeLoad || needsResave){
$TDP.writeFile(this.$token, configFile, JSON.stringify(this.config)); $TDP.writeFile(this.$token, configFile, JSON.stringify(this.config));
} }
} }
@@ -59,6 +64,8 @@ enabled(){
} }
else{ else{
$(document).one("dataSettingsValues", () => { $(document).one("dataSettingsValues", () => {
this.defaultConfig._theme = TD.settings.getTheme();
switch(TD.settings.getColumnWidth()){ switch(TD.settings.getColumnWidth()){
case "wide": this.defaultConfig.columnWidth = "350px"; break; case "wide": this.defaultConfig.columnWidth = "350px"; break;
case "narrow": this.defaultConfig.columnWidth = "270px"; break; case "narrow": this.defaultConfig.columnWidth = "270px"; break;
@@ -172,28 +179,44 @@ enabled(){
} }
// SELECTS // SELECTS
else if (tag === "SELECT"){ else if (tag === "SELECT"){
if (!item.val(me.config[key]).val()){ let optionCustom = item.find("option[value^='custom']");
let custom = item.find("option[value='custom']");
if (custom.length === 1){ let resetMyValue = () => {
item.val("custom"); if (!item.val(me.config[key]).val() && optionCustom.length === 1){
custom.text(getTextForCustom(key)); item.val(optionCustom.attr("value"));
optionCustom.text(getTextForCustom(key));
} }
} };
resetMyValue();
item.change(function(){ // TODO change doesn't fire when Custom is already selected item.change(function(){ // TODO change doesn't fire when Custom is already selected
let val = item.val(); let val = item.val();
if (val === "custom"){ if (val === "custom-px"){
val = prompt("Enter custom value:"); val = (prompt("Enter custom value (px):") || "").trim();
if (val){ if (val){
updateKey(key, val); if (val.endsWith("px")){
item.find("option[value='custom']").text(getTextForCustom(key)); val = val.slice(0, -2).trim();
}
if (/^[0-9]+$/.test(val)){
updateKey(key, val+"px");
optionCustom.text(getTextForCustom(key));
}
else{
alert("Invalid value, only px values are supported.");
resetMyValue();
}
}
else{
resetMyValue();
} }
} }
else{ else{
updateKey(key, item.val()); updateKey(key, item.val());
optionCustom.text("Custom");
} }
}); });
} }
@@ -217,9 +240,11 @@ enabled(){
modal.find("[data-td-theme='"+TD.settings.getTheme()+"']").prop("checked", true); modal.find("[data-td-theme='"+TD.settings.getTheme()+"']").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");
setTimeout(function(){ setTimeout(function(){
TD.settings.setTheme($(this).attr("data-td-theme"));
$(document).trigger("uiToggleTheme"); $(document).trigger("uiToggleTheme");
me.saveConfig();
me.reinjectAll(); me.reinjectAll();
}, 1); }, 1);
}); });
@@ -331,7 +356,7 @@ enabled(){
this.css.insert("#general_settings .cf { display: none !important }"); this.css.insert("#general_settings .cf { display: none !important }");
this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }"); this.css.insert("#settings-modal .js-setting-list li:nth-child(3) { border-bottom: 1px solid #ccd6dd }");
this.css.insert(".txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: "+this.config.fontSize+" !important }"); this.css.insert("html[data-td-font] { font-size: "+this.config.fontSize+" !important }");
this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }"); this.css.insert(".avatar { border-radius: "+this.config.avatarRadius+"% !important }");
let notificationScrollbarColor = null; let notificationScrollbarColor = null;
@@ -339,128 +364,140 @@ enabled(){
if (this.config.themeColorTweaks){ if (this.config.themeColorTweaks){
switch(TD.settings.getTheme()){ switch(TD.settings.getTheme()){
case "dark": case "dark":
this.css.insert(".app-content, .app-columns-container { background-color: #444448 }"); this.css.insert(".app-content, .app-columns-container { background-color: #444448 !important }");
this.css.insert(".column-drag-handle { opacity: 0.5 }"); this.css.insert(".column-drag-handle { opacity: 0.5 !important }");
this.css.insert(".column-drag-handle:hover { opacity: 1 }"); 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 }"); 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 }");
notificationScrollbarColor = "666"; notificationScrollbarColor = "666";
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 }"); 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(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 }"); this.css.insert(".app-columns-container.scroll-styled-h::-webkit-scrollbar-thumb:not(:hover) { background-color: #a5aeb5 !important }");
notificationScrollbarColor = "a5aeb5"; notificationScrollbarColor = "a5aeb5";
break; break;
} }
} }
if (this.config.showCharacterCount){
this.css.insert("#tduck .js-character-count.is-hidden { display: inline !important }");
}
if (this.config.hideTweetActions){ if (this.config.hideTweetActions){
this.css.insert(".tweet-action { opacity: 0; }"); this.css.insert(".tweet-action { opacity: 0; }");
this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5; }"); this.css.insert(".tweet-actions.is-visible .tweet-action { opacity: 0.5 }");
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }"); this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important }");
this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }"); this.css.insert(".tweet:hover .tweet-action, .tweet-action.is-selected, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1 !important; visibility: visible !important }");
} }
if (this.config.moveTweetActionsToRight){ if (this.config.moveTweetActionsToRight){
this.css.insert(".tweet-actions { float: right !important; width: auto !important }"); this.css.insert("#tduck .tweet-actions { float: right !important; width: auto !important }");
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important }"); this.css.insert("#tduck .tweet-actions > li:nth-child(4) { margin-right: 2px !important }");
}
if (this.config.increaseQuoteTextSize){
this.css.insert(".quoted-tweet { font-size: 1em !important }");
} }
if (this.config.smallComposeTextSize){ if (this.config.smallComposeTextSize){
this.css.insert(".compose-text { font-size: 12px !important; height: 120px !important }"); this.css.insert("#tduck .compose-text { font-size: 12px !important; height: 120px !important }");
} }
if (this.config.revertIcons){ if (this.config.revertIcons){
let iconData = [
[ ".icon-twitter-bird", "00" ],
[ ".icon-mention", "01" ],
[ ".icon-following", "02" ],
[ ".icon-message", "03" ],
[ ".icon-home", "04" ],
[ ".icon-hashtag", "05" ],
[ ".icon-reply", "06" ],
[ ".icon-favorite", "55" ],
[ ".icon-retweet", "08" ],
[ ".icon-drafts", "09" ],
[ ".icon-search", "0a" ],
[ ".icon-trash", "0c" ],
[ ".icon-close", "0d" ],
[ ".icon-arrow-r:before,.Icon--caretRight", "0e" ],
[ ".icon-arrow-l:before,.Icon--caretLeft", "0f" ],
[ ".icon-protected", "13" ],
[ ".icon-list", "14" ],
[ ".icon-camera", "15" ],
[ ".icon-more", "16" ],
[ ".icon-settings", "18" ],
[ ".icon-notifications", "19" ],
[ ".icon-user-dd", "1a" ],
[ ".icon-activity", "1c" ],
[ ".icon-trending", "1d" ],
[ ".icon-minus", "1e" ],
[ ".icon-plus", "1f" ],
[ ".icon-geo", "20" ],
[ ".icon-check", "21" ],
[ ".icon-schedule", "22" ],
[ ".icon-dot", "23" ],
[ ".icon-user", "24" ],
[ ".icon-content", "25" ],
[ ".icon-arrow-d:before,.Icon--caretDown", "26" ],
[ ".icon-arrow-u", "27" ],
[ ".icon-share", "28" ],
[ ".icon-info", "29" ],
[ ".icon-verified", "2a" ],
[ ".icon-translator", "2b" ],
[ ".icon-blocked", "2c" ],
[ ".icon-constrain", "2d" ],
[ ".icon-play-video", "2e" ],
[ ".icon-empty", "2f" ],
[ ".icon-clear-input", "30" ],
[ ".icon-compose", "31" ],
[ ".icon-mark-read", "32" ],
[ ".icon-arrow-r-double", "33" ],
[ ".icon-arrow-l-double", "34" ],
[ ".icon-follow", "35" ],
[ ".icon-image", "36" ],
[ ".icon-popout", "37" ],
[ ".icon-move", "39" ],
[ ".icon-compose-grid", "3a" ],
[ ".icon-compose-minigrid", "3b" ],
[ ".icon-compose-list", "3c" ],
[ ".icon-edit", "40" ],
[ ".icon-clear-timeline", "41" ],
[ ".icon-sliders", "42" ],
[ ".icon-custom-timeline", "43" ],
[ ".icon-compose-dm", "44" ],
[ ".icon-bg-dot", "45" ],
[ ".icon-user-team-mgr", "46" ],
[ ".icon-user-switch", "47" ],
[ ".icon-conversation", "48" ],
[ ".icon-dataminr", "49" ],
[ ".icon-link", "4a", ],
[ ".icon-flash", "50" ],
[ ".icon-pointer-u", "51" ],
[ ".icon-analytics", "54" ],
[ ".icon-heart", "55" ],
[ ".icon-calendar", "56" ],
[ ".icon-attachment", "57" ],
[ ".icon-play", "58" ],
[ ".icon-bookmark", "59" ],
[ ".icon-play-badge", "60" ],
[ ".icon-gif-badge", "61" ],
[ ".icon-poll", "62" ],
[ ".icon-heart-filled", "55" ],
[ ".icon-retweet-filled", "08" ],
[ ".icon-list-filled", "14" ],
[ ".icon-user-filled", "35" ],
];
this.icons = document.createElement("style"); this.icons = document.createElement("style");
this.icons.innerHTML = ` this.icons.innerHTML = `
@font-face { @font-face {
font-family: 'tweetdeckold'; font-family: '_of';
src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff"); src: url("https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff") format("woff");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
.icon-twitter-bird:before{content:"\\f000";font-family:tweetdeckold} ${iconData.map(entry => `#tduck ${entry[0]}:before{content:\"\\f0${entry[1]}\";font-family:_of!important}`).join("")}
.icon-mention:before{content:"\\f001";font-family:tweetdeckold}
.icon-following:before{content:"\\f002";font-family:tweetdeckold}
.icon-message:before{content:"\\f003";font-family:tweetdeckold}
.icon-home:before{content:"\\f004";font-family:tweetdeckold}
.icon-hashtag:before{content:"\\f005";font-family:tweetdeckold}
.icon-reply:before{content:"\\f006";font-family:tweetdeckold}
.icon-favorite:before{content:"\\f055";font-family:tweetdeckold}
.icon-retweet:before{content:"\\f008";font-family:tweetdeckold}
.icon-drafts:before{content:"\\f009";font-family:tweetdeckold}
.icon-search:before{content:"\\f00a";font-family:tweetdeckold}
.icon-trash:before{content:"\\f00c";font-family:tweetdeckold}
.icon-close:before{content:"\\f00d";font-family:tweetdeckold}
.icon-arrow-r:before,.Icon--caretRight:before{content:"\\f00e";font-family:tweetdeckold}
.icon-arrow-l:before,.Icon--caretLeft:before{content:"\\f00f";font-family:tweetdeckold}
.icon-protected:before{content:"\\f013";font-family:tweetdeckold}
.icon-list:before{content:"\\f014";font-family:tweetdeckold}
.icon-camera:before{content:"\\f015";font-family:tweetdeckold}
.icon-more:before{content:"\\f016";font-family:tweetdeckold}
.icon-settings:before{content:"\\f018";font-family:tweetdeckold}
.icon-notifications:before{content:"\\f019";font-family:tweetdeckold}
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold}
.icon-activity:before{content:"\\f01c";font-family:tweetdeckold}
.icon-trending:before{content:"\\f01d";font-family:tweetdeckold}
.icon-minus:before{content:"\\f01e";font-family:tweetdeckold}
.icon-plus:before{content:"\\f01f";font-family:tweetdeckold}
.icon-geo:before{content:"\\f020";font-family:tweetdeckold}
.icon-check:before{content:"\\f021";font-family:tweetdeckold}
.icon-schedule:before{content:"\\f022";font-family:tweetdeckold}
.icon-dot:before{content:"\\f023";font-family:tweetdeckold}
.icon-user:before{content:"\\f024";font-family:tweetdeckold}
.icon-content:before{content:"\\f025";font-family:tweetdeckold}
.icon-arrow-d:before,.Icon--caretDown:before{content:"\\f026";font-family:tweetdeckold}
.icon-arrow-u:before{content:"\\f027";font-family:tweetdeckold}
.icon-share:before{content:"\\f028";font-family:tweetdeckold}
.icon-info:before{content:"\\f029";font-family:tweetdeckold}
.icon-verified:before{content:"\\f02a";font-family:tweetdeckold}
.icon-translator:before{content:"\\f02b";font-family:tweetdeckold}
.icon-blocked:before{content:"\\f02c";font-family:tweetdeckold}
.icon-constrain:before{content:"\\f02d";font-family:tweetdeckold}
.icon-play-video:before{content:"\\f02e";font-family:tweetdeckold}
.icon-empty:before{content:"\\f02f";font-family:tweetdeckold}
.icon-clear-input:before{content:"\\f030";font-family:tweetdeckold}
.icon-compose:before{content:"\\f031";font-family:tweetdeckold}
.icon-mark-read:before{content:"\\f032";font-family:tweetdeckold}
.icon-arrow-r-double:before{content:"\\f033";font-family:tweetdeckold}
.icon-arrow-l-double:before{content:"\\f034";font-family:tweetdeckold}
.icon-follow:before{content:"\\f035";font-family:tweetdeckold}
.icon-image:before{content:"\\f036";font-family:tweetdeckold}
.icon-popout:before{content:"\\f037";font-family:tweetdeckold}
.icon-move:before{content:"\\f039";font-family:tweetdeckold}
.icon-compose-grid:before{content:"\\f03a";font-family:tweetdeckold}
.icon-compose-minigrid:before{content:"\\f03b";font-family:tweetdeckold}
.icon-compose-list:before{content:"\\f03c";font-family:tweetdeckold}
.icon-edit:before{content:"\\f040";font-family:tweetdeckold}
.icon-clear-timeline:before{content:"\\f041";font-family:tweetdeckold}
.icon-sliders:before{content:"\\f042";font-family:tweetdeckold}
.icon-custom-timeline:before{content:"\\f043";font-family:tweetdeckold}
.icon-compose-dm:before{content:"\\f044";font-family:tweetdeckold}
.icon-bg-dot:before{content:"\\f045";font-family:tweetdeckold}
.icon-user-team-mgr:before{content:"\\f046";font-family:tweetdeckold}
.icon-user-switch:before{content:"\\f047";font-family:tweetdeckold}
.icon-conversation:before{content:"\\f048";font-family:tweetdeckold}
.icon-dataminr:before{content:"\\f049";font-family:tweetdeckold}
.icon-link:before{content:"\\f04a";font-family:tweetdeckold}
.icon-flash:before{content:"\\f050";font-family:tweetdeckold}
.icon-pointer-u:before{content:"\\f051";font-family:tweetdeckold}
.icon-analytics:before{content:"\\f054";font-family:tweetdeckold}
.icon-heart:before{content:"\\f055";font-family:tweetdeckold}
.icon-calendar:before{content:"\\f056";font-family:tweetdeckold}
.icon-attachment:before{content:"\\f057";font-family:tweetdeckold}
.icon-play:before{content:"\\f058";font-family:tweetdeckold}
.icon-bookmark:before{content:"\\f059";font-family:tweetdeckold}
.icon-play-badge:before{content:"\\f060";font-family:tweetdeckold}
.icon-gif-badge:before{content:"\\f061";font-family:tweetdeckold}
.icon-poll:before{content:"\\f062";font-family:tweetdeckold}
.icon-heart-filled:before{content:"\\f055";font-family:tweetdeckold}
.icon-retweet-filled:before{content:"\\f008";font-family:tweetdeckold}
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold}
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold}
.drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important } .drawer .btn .icon, .app-header .btn .icon { line-height: 1em !important }
.column-header .column-type-icon { bottom: 26px !important } .column-header .column-type-icon { bottom: 26px !important }
@@ -507,21 +544,25 @@ enabled(){
$TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", ` $TDP.injectIntoNotificationsBefore(this.$token, "css", "</head>", `
<style type='text/css'> <style type='text/css'>
.txt-base-smallest:not(.icon), .txt-base-largest:not(.icon) { font-size: ${this.config.fontSize} !important } html[data-td-font] { font-size: ${this.config.fontSize} !important }
.avatar { border-radius: ${this.config.avatarRadius}% !important } .avatar { border-radius: ${this.config.avatarRadius}% !important }
${this.config.increaseQuoteTextSize ? `
.quoted-tweet { font-size: 1em !important }
` : ``}
${this.config.revertIcons ? ` ${this.config.revertIcons ? `
@font-face { font-family: 'tweetdeckold'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal } @font-face { font-family: '_of'; src: url(\"https://ton.twimg.com/tweetdeck-web/web/assets/fonts/tweetdeck-regular-webfont.5f4ea87976.woff\") format(\"woff\"); font-weight: normal; font-style: normal }
.icon-reply:before{content:"\\f006";font-family:tweetdeckold} #tduck .icon-reply:before{content:"\\f006";font-family:_of!important}
.icon-heart-filled:before{content:"\\f055";font-family:tweetdeckold} #tduck .icon-heart-filled:before{content:"\\f055";font-family:_of!important}
.icon-retweet-filled:before{content:"\\f008";font-family:tweetdeckold} #tduck .icon-retweet-filled:before{content:"\\f008";font-family:_of!important}
.icon-list-filled:before{content:"\\f014";font-family:tweetdeckold} #tduck .icon-list-filled:before{content:"\\f014";font-family:_of!important}
.icon-user-filled:before{content:"\\f035";font-family:tweetdeckold} #tduck .icon-user-filled:before{content:"\\f035";font-family:_of!important}
.icon-user-dd:before{content:"\\f01a";font-family:tweetdeckold} #tduck .icon-user-dd:before{content:"\\f01a";font-family:_of!important}
` : ``} ` : ``}
${notificationScrollbarColor ? ` ${notificationScrollbarColor ? `
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} } .scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb { background-color: #${notificationScrollbarColor} !important }
` : ``} ` : ``}
</style>`); </style>`);
}; };

View File

@@ -1,4 +1,4 @@
<div class="td-modal-panel js-modal-panel mdl s-tall-fixed is-inverted-dark"> <div id="edit-design-panel" class="js-modal-panel mdl s-tall-fixed is-inverted-dark">
<header class="js-mdl-header mdl-header js-drag-handle"> <header class="js-mdl-header mdl-header js-drag-handle">
<h3 class="mdl-header-title js-header-title">TweetDuck - Layout &amp; Design</h3> <h3 class="mdl-header-title js-header-title">TweetDuck - Layout &amp; Design</h3>
<a href="#" class="mdl-dismiss js-dismiss link-normal-dark"> <a href="#" class="mdl-dismiss js-dismiss link-normal-dark">
@@ -6,8 +6,8 @@
</a> </a>
</header> </header>
<div class="mdl-inner"> <div class="mdl-inner">
<div class="td-modal-content mdl-content js-mdl-content horizontal-flow-container"> <div id="edit-design-panel-content" class="mdl-content js-mdl-content horizontal-flow-container">
<div class="td-modal-inner-cols"> <div id="edit-design-panel-inner-cols">
<div class="l-column mdl-column"> <div class="l-column mdl-column">
<!-- THEME --> <!-- THEME -->
@@ -36,7 +36,7 @@
<option value="310px">Medium (310px)</option> <option value="310px">Medium (310px)</option>
<option value="350px">Wide (350px)</option> <option value="350px">Wide (350px)</option>
<option value="400px">Extreme (400px)</option> <option value="400px">Extreme (400px)</option>
<option value="custom">Custom</option> <option value="custom-px">Custom</option>
</optgroup> </optgroup>
<option disabled></option> <option disabled></option>
<optgroup label="Dynamic width"> <optgroup label="Dynamic width">
@@ -59,17 +59,15 @@
<option value="14px">Medium (14px)</option> <option value="14px">Medium (14px)</option>
<option value="15px">Large (15px)</option> <option value="15px">Large (15px)</option>
<option value="16px">Largest (16px)</option> <option value="16px">Largest (16px)</option>
<option value="custom">Custom</option> <option value="custom-px">Custom</option>
</select> </select>
<label class="checkbox">
<!-- ADVANCED --> <input data-td-key="increaseQuoteTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
Increase quoted tweet font size
<label class="txt-uppercase touch-larger-label">
<b>Advanced</b>
</label> </label>
<label class="checkbox"> <label class="checkbox">
<input data-td-key="optimizeAnimations" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox">
Use more memory for smoother animations Small tweet input font size
</label> </label>
</div> </div>
@@ -94,17 +92,27 @@
<label class="txt-uppercase touch-larger-label"> <label class="txt-uppercase touch-larger-label">
<b>Design</b> <b>Design</b>
</label> </label>
<label class="checkbox">
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
Theme color tweaks
</label>
<label class="checkbox"> <label class="checkbox">
<input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="revertIcons" class="js-theme-checkbox touch-larger-label" type="checkbox">
Revert icon design Revert icon design
</label> </label>
<label class="checkbox"> <label class="checkbox">
<input data-td-key="smallComposeTextSize" class="js-theme-checkbox touch-larger-label" type="checkbox"> <input data-td-key="showCharacterCount" class="js-theme-checkbox touch-larger-label" type="checkbox">
Small compose tweet font size Always show character count
</label>
<label class="checkbox">
<input data-td-key="themeColorTweaks" class="js-theme-checkbox touch-larger-label" type="checkbox">
Theme color tweaks
</label>
<!-- ADVANCED -->
<label class="txt-uppercase touch-larger-label">
<b>Advanced</b>
</label>
<label class="checkbox">
<input data-td-key="optimizeAnimations" class="js-theme-checkbox touch-larger-label" type="checkbox">
Use more memory for smoother animations
</label> </label>
</div> </div>
@@ -151,54 +159,55 @@
/* Containers */ /* Containers */
.td-modal-panel { #edit-design-panel {
width: 693px; width: 693px;
height: 380px; height: 380px;
} }
.td-modal-inner-cols { #edit-design-panel-inner-cols {
padding: 0 6px; padding: 0 6px;
} }
.td-modal-inner-cols .l-column { #edit-design-panel-inner-cols .l-column {
padding: 15px 9px; padding: 15px 9px;
box-sizing: border-box; box-sizing: border-box;
width: 225px;
font-size: 0; /* fix custom font size breaking the modal layout */ font-size: 0; /* fix custom font size breaking the modal layout */
} }
.td-modal-inner-cols .l-column:nth-child(2) { #edit-design-panel-inner-cols .l-column:nth-child(3) {
width: 250px; width: 200px;
}
.td-modal-inner-full {
padding: 15px;
}
.td-modal-inner-full .txt-center {
margin-bottom: 10px;
} }
/* Elements */ /* Elements */
.td-modal-content label { #edit-design-panel-content label.txt-uppercase {
margin-top: 18px; margin-top: 18px;
} }
.td-modal-content label:first-child { #edit-design-panel-content label.txt-uppercase:first-child {
margin-top: 0; margin-top: 0;
} }
.td-modal-content label.radio { #edit-design-panel-content label.radio {
display: inline-block; display: inline-block;
margin: 0 16px 5px 4px; margin: 0 16px 5px 4px;
cursor: pointer; cursor: pointer;
} }
.td-modal-content label.checkbox { #edit-design-panel-content label.checkbox {
margin: 0 0 5px 4px; margin: 0 0 5px 4px;
cursor: pointer; cursor: pointer;
} }
#edit-design-panel-content select + label.checkbox {
margin-top: 9px;
}
#edit-design-panel-content input.js-theme-checkbox, #edit-design-panel-content input.js-theme-radio {
margin-top: 1px;
}
/* Avatar shape */ /* Avatar shape */
.td-avatar-shape-container { .td-avatar-shape-container {

View File

@@ -9,7 +9,7 @@ Emoji keyboard
chylex chylex
[version] [version]
1.4 1.4.1
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -303,23 +303,26 @@ enabled(){
let firstColon = val.lastIndexOf(':', ele[0].selectionStart); let firstColon = val.lastIndexOf(':', ele[0].selectionStart);
return if firstColon === -1; return if firstColon === -1;
let search = val.substring(firstColon+1, ele[0].selectionStart); let search = val.substring(firstColon+1, ele[0].selectionStart).toLowerCase();
return if !/^[a-z_]+$/i.test(search); return if !/^[a-z_]+$/.test(search);
let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase()); let keywords = search.split("_").filter(kw => kw.length > 0).map(kw => kw.toLowerCase());
return if keywords.length === 0; return if keywords.length === 0;
let foundName = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw))); let foundNames = me.emojiNames.filter(name => keywords.every(kw => name.includes(kw)));
if (foundName.length === 0){ if (foundNames.length === 0){
return; return;
} }
else if (foundNames.length > 1 && foundNames.includes(search)){
foundNames = [ search ];
}
lastEmojiKeyword = `:${search}:`; lastEmojiKeyword = `:${search}:`;
lastEmojiPosition = lastEmojiLength = 0; lastEmojiPosition = lastEmojiLength = 0;
if (foundName.length === 1){ if (foundNames.length === 1){
let foundIndex = me.emojiNames.indexOf(foundName[0]); let foundIndex = me.emojiNames.indexOf(foundNames[0]);
let foundEmoji; let foundEmoji;
for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){ for(let array of [ me.emojiData1, me.emojiData2[me.selectedSkinTone], me.emojiData3 ]){
@@ -346,7 +349,7 @@ enabled(){
lastEmojiLength = foundEmoji.length; lastEmojiLength = foundEmoji.length;
} }
} }
else if (foundName.length > 1){ else if (foundNames.length > 1){
e.preventDefault(); e.preventDefault();
ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart)); ele.val(val.substring(0, firstColon)+val.substring(ele[0].selectionStart));
ele[0].selectionEnd = ele[0].selectionStart = firstColon; ele[0].selectionEnd = ele[0].selectionStart = firstColon;

View File

@@ -8,7 +8,7 @@ Custom reply account
chylex chylex
[version] [version]
1.2.2 1.2.4
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com
@@ -20,4 +20,4 @@ configuration.js
configuration.default.js configuration.default.js
[requires] [requires]
1.3.3 1.10.3

View File

@@ -6,7 +6,7 @@ enabled(){
this.lastSelectedAccount = null; this.lastSelectedAccount = null;
this.uiComposeTweetEvent = (e, data) => { this.uiComposeTweetEvent = (e, data) => {
return if data.type !== "reply" || data.popFromInline || !("element" in data); return if !(data.type === "reply" || (data.type === "tweet" && "quotedTweet" in data)) || data.popFromInline || !("element" in data);
var query; var query;
@@ -17,7 +17,7 @@ enabled(){
return; return;
} }
var section = data.element.closest("section.column"); var section = data.element.closest("section.js-column");
var column = TD.controller.columnManager.get(section.attr("data-column")); var column = TD.controller.columnManager.get(section.attr("data-column"));
var header = $(".column-title", section); var header = $(".column-title", section);
@@ -35,7 +35,7 @@ enabled(){
} }
try{ try{
query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column); query = configuration.customSelector(column.getColumnType(), columnTitle, columnAccount, column, section.hasClass("column-temp"));
}catch(e){ }catch(e){
$TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message); $TD.alert("warning", "Plugin reply-account has invalid configuration: customSelector threw an error: "+e.message);
return; return;
@@ -117,19 +117,9 @@ enabled(){
} }
ready(){ ready(){
var events = $._data(document, "events"); for(let event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
for(var event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
$(document).on(event, this.uiComposeTweetEvent); $(document).on(event, this.uiComposeTweetEvent);
window.TDPF_prioritizeNewestEvent(document, event);
var handlers = events[event];
var newHandler = handlers[handlers.length-1];
for(var index = handlers.length-1; index > 0; index--){
handlers[index] = handlers[index-1];
}
handlers[0] = newHandler;
} }
$(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged); $(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);

View File

@@ -21,11 +21,14 @@
* This assumes a basic knowledge of JavaScript and jQuery. * This assumes a basic knowledge of JavaScript and jQuery.
* *
* 1. Set value of 'useAdvancedSelector' to true * 1. Set value of 'useAdvancedSelector' to true
* 2. Uncomment the 'customSelector' function, and replace the example code with your desired behavior * 2. Replace the example code in 'customSelector' function with your desired behavior
* *
* The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'. * The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'.
* If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior. * If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior.
* *
* When writing a custom function, you can install Dev Tools to access the browser console:
* https://tweetduck.chylex.com/guide/#dev-tools
*
* *
* The 'type' parameter is TweetDeck column type. Here is the full list of column types, note that some are * The 'type' parameter is TweetDeck column type. Here is the full list of column types, note that some are
* unused and have misleading names (for example, Home columns are 'col_timeline' instead of 'col_home'): * unused and have misleading names (for example, Home columns are 'col_timeline' instead of 'col_home'):
@@ -46,28 +49,33 @@
* contain other text (for example, the Scheduled column contains the string 'All accounts'). * contain other text (for example, the Scheduled column contains the string 'All accounts').
* *
* *
* The 'column' parameter is a TweetDeck column object. If you want to see all properties of the object, * The 'column' parameter is a TweetDeck column object. If you want to see the object structure,
* run the following code in your browser console, which will return an array containing all of your * run the following code in the console for an array containing all of your column objects:
* current column objects in order:
* TD.controller.columnManager.getAllOrdered() * TD.controller.columnManager.getAllOrdered()
* *
*
* The 'isTemporary' parameter is true if the column is not attached to the main column list,
* for example when clicking on a profile and viewing their tweets in a modal dialog.
*
*/ */
useAdvancedSelector: false, useAdvancedSelector: false,
/*customSelector: function(type, title, account, column){ customSelector: function(type, title, account, column, isTemporary){
console.info(arguments); // Prints all arguments into the console
if (type === "col_search" && title === "TweetDuck"){ if (type === "col_search" && title === "TweetDuck"){
// This is a search column that looks for 'TweetDuck' in the tweets, // This is a search column that looks for 'TweetDuck' in the tweets,
// search columns are normally linked to the preferred account // search columns are normally linked to the preferred account
// so this forces the @TryTweetDuck account to be used instead. // so this forces the @TryTweetDuck account to be used instead
return "@TryTweetDuck"; return "@TryTweetDuck";
} }
else if (type === "col_timeline" && account === "@chylexcz"){ else if (type === "col_timeline" && account === "@chylexcz"){
// This is a Home column of my test account @chylexcz, // This is a Home column of my test account @chylexcz,
// but I want to reply to tweets from my official account. // but I want to reply to tweets from my official account
return "@chylexmc"; return "@chylexmc";
} }
// otherwise returns 'undefined' which falls back to 'defaultAccount' behavior // otherwise returns 'undefined' which falls back to 'defaultAccount' behavior
}*/ }
} }

View File

@@ -8,7 +8,7 @@ Templates
chylex chylex
[version] [version]
1.0.2 1.0.3
[website] [website]
https://tweetduck.chylex.com https://tweetduck.chylex.com

View File

@@ -50,25 +50,25 @@ enabled(){
this.css = window.TDPF_createCustomStyle(this); this.css = window.TDPF_createCustomStyle(this);
this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }"); this.css.insert(".manage-templates-btn.active { color: #fff; box-shadow: 0 0 2px 3px #50a5e6; outline: 0; }");
this.css.insert(".templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }"); this.css.insert("#templates-modal-wrap { width: 100%; height: 100%; padding: 49px; position: absolute; z-index: 999; box-sizing: border-box; background-color: rgba(0, 0, 0, 0.5); }");
this.css.insert(".templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }"); this.css.insert("#templates-modal { width: 100%; height: 100%; background-color: #fff; display: flex; }");
this.css.insert(".templates-modal > div { display: flex; flex-direction: column; }"); this.css.insert("#templates-modal > div { display: flex; flex-direction: column; }");
this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }"); this.css.insert(".templates-modal-bottom { flex: 0 0 auto; padding: 16px; }");
this.css.insert(".template-list .templates-modal-bottom { display: flex; justify-content: space-between; }"); this.css.insert("#template-list .templates-modal-bottom { display: flex; justify-content: space-between; }");
this.css.insert(".template-editor .templates-modal-bottom { text-align: right; }"); this.css.insert("#template-editor .templates-modal-bottom { text-align: right; }");
this.css.insert(".template-list { height: 100%; flex: 1 1 auto; }"); this.css.insert("#template-list { height: 100%; flex: 1 1 auto; }");
this.css.insert(".template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }"); this.css.insert("#template-list ul { list-style-type: none; font-size: 24px; color: #222; flex: 1 1 auto; padding: 12px; overflow-y: auto; }");
this.css.insert(".template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }"); this.css.insert("#template-list li { display: block; width: 100%; padding: 4px 8px; box-sizing: border-box; }");
this.css.insert(".template-list li[data-template] { cursor: pointer; }"); this.css.insert("#template-list li[data-template] { cursor: pointer; }");
this.css.insert(".template-list li[data-template]:hover { background-color: #d8d8d8; }"); this.css.insert("#template-list li[data-template]:hover { background-color: #d8d8d8; }");
this.css.insert(".template-list li span { white-space: nowrap; }"); this.css.insert("#template-list li span { white-space: nowrap; }");
this.css.insert(".template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }"); this.css.insert("#template-list li .icon { opacity: 0.6; margin-left: 4px; padding: 3px; }");
this.css.insert(".template-list li .icon:hover { opacity: 1; }"); this.css.insert("#template-list li .icon:hover { opacity: 1; }");
this.css.insert(".template-list li .template-actions { float: right; }"); this.css.insert("#template-list li .template-actions { float: right; }");
this.css.insert(".template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }"); this.css.insert("#template-editor { height: 100%; flex: 0 0 auto; width: 25vw; min-width: 150px; max-width: 400px; background-color: #485865; }");
this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }"); this.css.insert(".template-editor-form { flex: 1 1 auto; padding: 12px 16px; font-size: 14px; overflow-y: auto; }");
this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }"); this.css.insert(".template-editor-form .compose-text-title { margin: 24px 0 9px; }");
this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }"); this.css.insert(".template-editor-form .compose-text-title:first-child { margin-top: 0; }");
@@ -261,9 +261,9 @@ enabled(){
$(".manage-templates-btn").addClass("active"); $(".manage-templates-btn").addClass("active");
let html = ` let html = `
<div class="templates-modal-wrap"> <div id="templates-modal-wrap">
<div class="templates-modal"> <div id="templates-modal">
<div class="template-list"> <div id="template-list">
<ul></ul> <ul></ul>
<div class="templates-modal-bottom"> <div class="templates-modal-bottom">
@@ -272,7 +272,7 @@ enabled(){
</div> </div>
</div> </div>
<div class="template-editor invisible"> <div id="template-editor" class="invisible">
<div class="template-editor-form"> <div class="template-editor-form">
<div class="compose-text-title">Template Name</div> <div class="compose-text-title">Template Name</div>
<input name="template-name" type="text"> <input name="template-name" type="text">
@@ -320,7 +320,7 @@ enabled(){
$(".js-app-content").prepend(html); $(".js-app-content").prepend(html);
let ele = $(".templates-modal-wrap").first(); let ele = $("#templates-modal-wrap").first();
ele.on("click", "li[data-template]", function(e){ ele.on("click", "li[data-template]", function(e){
let template = me.config.templates[$(this).attr("data-template")]; let template = me.config.templates[$(this).attr("data-template")];
@@ -332,7 +332,7 @@ enabled(){
switch($(this).attr("data-action")){ switch($(this).attr("data-action")){
case "edit-template": case "edit-template":
let editor = $(".template-editor"); let editor = $("#template-editor");
if (editor.hasClass("invisible")){ if (editor.hasClass("invisible")){
toggleEditor(); toggleEditor();
@@ -372,7 +372,7 @@ enabled(){
break; break;
case "editor-confirm": case "editor-confirm":
let editor = $(".template-editor"); let editor = $("#template-editor");
if (me.editingTemplate !== null){ if (me.editingTemplate !== null){
delete me.config.templates[me.editingTemplate]; delete me.config.templates[me.editingTemplate];
@@ -408,15 +408,15 @@ enabled(){
}; };
var hideTemplateModal = function(){ var hideTemplateModal = function(){
$(".templates-modal-wrap").remove(); $("#templates-modal-wrap").remove();
$(".manage-templates-btn").removeClass("active"); $(".manage-templates-btn").removeClass("active");
}; };
var toggleEditor = function(){ var toggleEditor = function(){
let editor = $(".template-editor"); let editor = $("#template-editor");
$("[name]", editor).val(""); $("[name]", editor).val("");
if ($("button[data-action='new-template']", ".template-list").add(editor).toggleClass("invisible").hasClass("invisible")){ if ($("button[data-action='new-template']", "#template-list").add(editor).toggleClass("invisible").hasClass("invisible")){
me.editingTemplate = null; me.editingTemplate = null;
} }
}; };
@@ -435,7 +435,7 @@ enabled(){
eles.push("<li>No templates available</li>"); eles.push("<li>No templates available</li>");
} }
$(".template-list").children("ul").html(eles.join("")); $("#template-list").children("ul").html(eles.join(""));
if (save){ if (save){
this.saveConfig(); this.saveConfig();
@@ -445,7 +445,7 @@ enabled(){
// event handlers // event handlers
this.manageTemplatesButtonClickEvent = function(e){ this.manageTemplatesButtonClickEvent = function(e){
if ($(".templates-modal-wrap").length){ if ($("#templates-modal-wrap").length){
hideTemplateModal(); hideTemplateModal();
} }
else{ else{
@@ -471,7 +471,7 @@ disabled(){
this.css.remove(); this.css.remove();
$(".manage-templates-btn").remove(); $(".manage-templates-btn").remove();
$(".templates-modal-wrap").remove(); $("#templates-modal-wrap").remove();
$(document).off("uiDrawerActive", this.drawerToggleEvent); $(document).off("uiDrawerActive", this.drawerToggleEvent);

View File

@@ -1,8 +1,4 @@
enabled(){ enabled(){
this.reloadColumns = () => {
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
};
// styles // styles
this.css = window.TDPF_createCustomStyle(this); this.css = window.TDPF_createCustomStyle(this);
@@ -10,17 +6,17 @@ enabled(){
// utility functions // utility functions
var hasPoll = function(tweet){ const hasPoll = function(tweet){
return tweet.hasPoll && tweet.hasPoll(); return tweet.hasPoll && tweet.hasPoll();
}; };
var renderTweetPoll = function(tweet){ const renderTweetPoll = function(tweet){
return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, { return `<div class='td-timeline-poll'>${TD.ui.template.render("status/poll", $.extend({}, tweet, {
chirp: tweet chirp: tweet
}))}</div>`; }))}</div>`;
}; };
var renderPollHook = function(tweet, html){ const renderPollHook = function(tweet, html){
let ele = null; let ele = null;
if (hasPoll(tweet)){ if (hasPoll(tweet)){
@@ -67,7 +63,7 @@ enabled(){
}; };
this.prevRenderFuncs = funcs; this.prevRenderFuncs = funcs;
this.reloadColumns(); window.TDPF_reloadColumns();
} }
disabled(){ disabled(){
@@ -76,5 +72,5 @@ disabled(){
TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView; TD.components.TweetDetailView.prototype._renderChirp = this.prevRenderFuncs.TweetDetailView;
this.css.remove(); this.css.remove();
this.reloadColumns(); window.TDPF_reloadColumns();
} }

View File

@@ -3,6 +3,18 @@ $ErrorActionPreference = "Stop"
Set-Location $dir Set-Location $dir
function Check-Carriage-Return{
Param([Parameter(Mandatory = $True, Position = 1)] $fname)
$file = @(Get-ChildItem -Include $fname -Recurse)[0]
if ((Get-Content -Path $file.FullName -Raw).Contains("`r")){
Throw "$fname cannot contain carriage return"
}
Write-Host "Verified" $file.FullName.Substring($dir.Length)
}
function Rewrite-File{ function Rewrite-File{
[CmdletBinding()] [CmdletBinding()]
Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file) Param([Parameter(Mandatory = $True, ValueFromPipeline = $True)][array] $lines, [Parameter(Mandatory = $True, Position = 1)] $file)
@@ -11,23 +23,31 @@ function Rewrite-File{
Write-Host "Processed" $file.FullName.Substring($dir.Length) Write-Host "Processed" $file.FullName.Substring($dir.Length)
} }
ForEach($file in Get-ChildItem -Include *.js -Exclude configuration.default.js -Recurse){ try{
$lines = Get-Content -Path $file.FullName Check-Carriage-Return("emoji-ordering.txt")
$lines = $lines | % { $_.TrimStart() }
$lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
$lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
,$lines | Rewrite-File $file
}
ForEach($file in Get-ChildItem -Include *.css -Recurse){ ForEach($file in Get-ChildItem -Filter *.js -Exclude configuration.default.js -Recurse){
$lines = Get-Content -Path $file.FullName $lines = Get-Content -Path $file.FullName
$lines = $lines -Replace '\s*/\*.*?\*/', '' $lines = $lines | % { $_.TrimStart() }
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;' $lines = $lines -Replace '^(.*?)((?<=^|[;{}()])\s?//(?:\s.*|$))?$', '$1'
$lines = $lines -Replace '^(\S.*?) {$', '$1{' $lines = $lines -Replace '(?<!\w)return(\s.*?)? if (.*?);', 'if ($2)return$1;'
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file ,$lines | Rewrite-File $file
} }
ForEach($file in Get-ChildItem -Include *.html -Recurse){ ForEach($file in Get-ChildItem -Filter *.css -Recurse){
$lines = Get-Content -Path $file.FullName $lines = Get-Content -Path $file.FullName
,($lines | % { $_.TrimStart() }) | Rewrite-File $file $lines = $lines -Replace '\s*/\*.*?\*/', ''
$lines = $lines -Replace '^\s+(.+):\s?(.+?)(?:\s?(!important))?;$', '$1:$2$3;'
$lines = $lines -Replace '^(\S.*?) {$', '$1{'
@(($lines | Where { $_ -ne '' }) -Join ' ') | Rewrite-File $file
}
ForEach($file in Get-ChildItem -Filter *.html -Recurse){
$lines = Get-Content -Path $file.FullName
,($lines | % { $_.TrimStart() }) | Rewrite-File $file
}
}catch{
Write-Host "Encountered an error while running PostBuild.ps1 on line" $_.InvocationInfo.ScriptLineNumber
Write-Host $_
Exit 1
} }

View File

@@ -14,10 +14,15 @@
// //
var onAppReady = []; var onAppReady = [];
//
// Variable: DOM HTML element.
//
const doc = document.documentElement;
// //
// Variable: DOM object containing the main app element. // Variable: DOM object containing the main app element.
// //
var app = $(document.body).children(".js-app"); const app = $(document.body).children(".js-app");
// //
// Constant: Column types mapped to their titles. // Constant: Column types mapped to their titles.
@@ -45,7 +50,7 @@
// //
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled. // Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
// //
var prependToFunction = function(func, extension){ const prependToFunction = function(func, extension){
return function(){ return function(){
return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments); return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
}; };
@@ -54,7 +59,7 @@
// //
// Function: Appends code at the end of a function. // Function: Appends code at the end of a function.
// //
var appendToFunction = function(func, extension){ const appendToFunction = function(func, extension){
return function(){ return function(){
let res = func.apply(this, arguments); let res = func.apply(this, arguments);
extension.apply(this, arguments); extension.apply(this, arguments);
@@ -65,7 +70,7 @@
// //
// Function: Returns true if an object has a specified property, otherwise returns false without throwing an error. // Function: Returns true if an object has a specified property, otherwise returns false without throwing an error.
// //
var ensurePropertyExists = function(obj, ...chain){ const ensurePropertyExists = function(obj, ...chain){
for(let index = 0; index < chain.length; index++){ for(let index = 0; index < chain.length; index++){
if (!obj.hasOwnProperty(chain[index])){ if (!obj.hasOwnProperty(chain[index])){
$TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join(".")); $TD.crashDebug("Missing property "+chain[index]+" in chain [obj]."+chain.join("."));
@@ -81,7 +86,7 @@
// //
// Function: Retrieves a property of an element with a specified class. // Function: Retrieves a property of an element with a specified class.
// //
var getClassStyleProperty = function(cls, property){ const getClassStyleProperty = function(cls, property){
let column = document.createElement("div"); let column = document.createElement("div");
column.classList.add(cls); column.classList.add(cls);
column.style.display = "none"; column.style.display = "none";
@@ -96,7 +101,7 @@
// //
// Function: Event callback for a new tweet. // Function: Event callback for a new tweet.
// //
var onNewTweet = (function(){ const onNewTweet = (function(){
let recentMessages = new Set(); let recentMessages = new Set();
let recentTweets = new Set(); let recentTweets = new Set();
let recentTweetTimer = null; let recentTweetTimer = null;
@@ -123,7 +128,7 @@
}; };
let fixMedia = (html, media) => { let fixMedia = (html, media) => {
return html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.thumb()+'")').removeClass("is-zoomable"); return html.find(".js-media a[data-media-entity-id='"+media.mediaId+"']").css("background-image", 'url("'+media.small()+'")').removeClass("is-zoomable");
}; };
return function(column, tweet){ return function(column, tweet){
@@ -214,7 +219,7 @@
// Function: Shows tweet detail, used in notification context menu. // Function: Shows tweet detail, used in notification context menu.
// //
(function(){ (function(){
var showTweetDetailInternal = function(column, chirp){ const showTweetDetailInternal = function(column, chirp){
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp); TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
TD.controller.columnManager.showColumn(column.model.privateState.key); TD.controller.columnManager.showColumn(column.model.privateState.key);
@@ -258,49 +263,65 @@
})(); })();
// //
// Function: Retrieves the tags to be put into <head> for notification HTML code. // Block: Hook into settings object to detect when the settings change, and update html attributes and notification layout.
// //
var getNotificationHeadContents = function(){ (function(){
let tags = []; const refreshSettings = function(){
let fontSizeName = TD.settings.getFontSize();
let themeName = TD.settings.getTheme();
$(document.head).children("link[rel='stylesheet']:not([title]),link[title='"+TD.settings.getTheme()+"'],meta[charset],meta[http-equiv]").each(function(){ let tags = [
tags.push($(this)[0].outerHTML); "<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(){
tags.push($(this)[0].outerHTML);
});
tags.push("<style type='text/css'>");
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" !important }"); // set background color
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(".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 { 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 in notifications
if (fontSizeName === "smallest"){
tags.push(".badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
}
tags.push("</style>");
doc.setAttribute("data-td-font", fontSizeName);
doc.setAttribute("data-td-theme", themeName);
$TD.loadNotificationLayout(fontSizeName, tags.join(""));
};
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
setTimeout(refreshSettings, 0);
}); });
tags.push("<style type='text/css'>"); TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){
tags.push("body { background: "+getClassStyleProperty("column", "background-color")+" }"); // set background color setTimeout(refreshSettings, 0);
tags.push("a[data-full-url] { word-break: break-all }"); // break long urls });
tags.push(".txt-base-smallest .badge-verified:before { width: 13px !important; height: 13px !important; background-position: -223px -98px !important }"); // fix cut off badge icon
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(".activity-header { align-items: center !important; margin-bottom: 4px }"); // tweak alignment of avatar and text in notifications
tags.push(".activity-header .tweet-timestamp { line-height: unset }"); // fix timestamp position in notifications
tags.push("</style>");
return tags.join(""); onAppReady.push(refreshSettings);
}; })();
// //
// Block: Hook into settings object to detect when the settings change. // Block: Fix OS name and add ID to the document for priority CSS selectors.
// //
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){ if (ensurePropertyExists(TD, "util", "getOSName")){
$TD.loadFontSizeClass(name); TD.util.getOSName = function(){
}); return "windows";
};
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(name){ doc.classList.remove("os-");
document.documentElement.setAttribute("data-td-theme", name); doc.classList.add("os-windows");
}
setTimeout(function(){ doc.id = "tduck";
$TD.loadNotificationHeadContents(getNotificationHeadContents());
}, 0);
});
onAppReady.push(function(){
document.documentElement.setAttribute("data-td-theme", TD.settings.getTheme());
$TD.loadFontSizeClass(TD.settings.getFontSize());
$TD.loadNotificationHeadContents(getNotificationHeadContents());
});
// //
// Block: Enable popup notifications. // Block: Enable popup notifications.
@@ -357,7 +378,7 @@
mouseenter: function(){ mouseenter: function(){
let me = $(this); let me = $(this);
let text = me.text(); let text = me.text();
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){ tooltipTimer = window.setTimeout(function(){
@@ -399,7 +420,7 @@
})(); })();
// //
// Block: Bypass t.co when clicking links and media. // Block: Bypass t.co when clicking/dragging links and media.
// //
$(document.body).delegate("a[data-full-url]", "click auxclick", function(e){ $(document.body).delegate("a[data-full-url]", "click auxclick", function(e){
if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick if (e.button === 0 || e.button === 1){ // event.which seems to be borked in auxclick
@@ -408,8 +429,18 @@
} }
}); });
$(document.body).delegate("a[data-full-url]", "dragstart", function(e){
let url = $(this).attr("data-full-url");
let data = e.originalEvent.dataTransfer;
data.clearData();
data.setData("text/uri-list", url);
data.setData("text/plain", url);
data.setData("text/html", `<a href="${url}">${url}</a>`);
});
if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){ if (ensurePropertyExists(TD, "services", "TwitterUser", "prototype", "fromJSONObject")){
let prevFunc = TD.services.TwitterUser.prototype.fromJSONObject; const prevFunc = TD.services.TwitterUser.prototype.fromJSONObject;
TD.services.TwitterUser.prototype.fromJSONObject = function(){ TD.services.TwitterUser.prototype.fromJSONObject = function(){
let obj = prevFunc.apply(this, arguments); let obj = prevFunc.apply(this, arguments);
@@ -424,7 +455,7 @@
} }
if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){ if (ensurePropertyExists(TD, "services", "TwitterMedia", "prototype", "fromMediaEntity")){
let prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity; const prevFunc = TD.services.TwitterMedia.prototype.fromMediaEntity;
TD.services.TwitterMedia.prototype.fromMediaEntity = function(){ TD.services.TwitterMedia.prototype.fromMediaEntity = function(){
let obj = prevFunc.apply(this, arguments); let obj = prevFunc.apply(this, arguments);
@@ -479,13 +510,13 @@
(function(){ (function(){
var lastTweet = ""; var lastTweet = "";
var updateHighlightedColumn = function(ele){ const updateHighlightedColumn = function(ele){
highlightedColumnEle = ele; highlightedColumnEle = ele;
highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null; highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
return !!highlightedColumnObj; return !!highlightedColumnObj;
}; };
var updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){ const updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
highlightedTweetEle = ele; highlightedTweetEle = ele;
highlightedTweetObj = obj; highlightedTweetObj = obj;
@@ -495,7 +526,7 @@
} }
}; };
var processMedia = function(chirp){ const processMedia = function(chirp){
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";"); return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https+":small").join(";");
}; };
@@ -544,7 +575,7 @@
(function(){ (function(){
var selectedTweet; var selectedTweet;
var setImportantProperty = function(obj, property, value){ const setImportantProperty = function(obj, property, value){
if (obj.length === 1){ if (obj.length === 1){
obj[0].style.setProperty(property, value, "important"); obj[0].style.setProperty(property, value, "important");
} }
@@ -580,7 +611,7 @@
setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "4px"); setImportantProperty(selectedTweet.find(".js-media-preview-container"), "margin-bottom", "4px");
selectedTweet.find(".js-translate-call-to-action").first().remove(); selectedTweet.find(".js-translate-call-to-action").first().remove();
selectedTweet.find(".js-tweet").first().nextAll().remove(); selectedTweet.find(".js-tweet").first().nextAll().remove();
selectedTweet.find("footer").last().prevUntil(":not(.txt-mute.txt-small)").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"), "margin-bottom", "10px");
@@ -613,7 +644,7 @@
// Block: Paste images when tweeting. // Block: Paste images when tweeting.
// //
onAppReady.push(function(){ onAppReady.push(function(){
var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context; const uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){ app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
for(let item of e.originalEvent.clipboardData.items){ for(let item of e.originalEvent.clipboardData.items){
@@ -631,25 +662,41 @@
}); });
}); });
//
// Block: Setup a global function to reorder event priority.
//
window.TDGF_prioritizeNewestEvent = function(element, event){
let events = $._data(element, "events");
let handlers = events[event];
let newHandler = handlers[handlers.length-1];
for(let index = handlers.length-1; index > 0; index--){
handlers[index] = handlers[index-1];
}
handlers[0] = newHandler;
};
// //
// Block: Support for extra mouse buttons. // Block: Support for extra mouse buttons.
// //
(function(){ (function(){
var tryClickSelector = function(selector, parent){ const tryClickSelector = function(selector, parent){
return $(selector, parent).click().length; return $(selector, parent).click().length;
}; };
var tryCloseModal1 = function(){ const tryCloseModal1 = function(){
let modal = $("#open-modal"); let modal = $("#open-modal");
return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal); return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal);
}; };
var tryCloseModal2 = function(){ const tryCloseModal2 = function(){
let modal = $(".js-modals-container"); let modal = $(".js-modals-container");
return modal.length && tryClickSelector("a.mdl-dismiss", modal); return modal.length && tryClickSelector("a.mdl-dismiss", modal);
}; };
var tryCloseHighlightedColumn = function(){ const tryCloseHighlightedColumn = function(){
if (highlightedColumnEle){ if (highlightedColumnEle){
let column = highlightedColumnEle.closest(".js-column"); let column = highlightedColumnEle.closest(".js-column");
return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column)); return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
@@ -684,7 +731,7 @@
let isDraggingValid = false; let isDraggingValid = false;
window.TDGF_onGlobalDragStart = function(type, data){ window.TDGF_onGlobalDragStart = function(type, data){
isDraggingValid = type === "link" && tweetRegex.test(data); isDraggingValid = (type === "link" || type === "text") && tweetRegex.test(data);
}; };
app.delegate("section.js-column", { app.delegate("section.js-column", {
@@ -737,14 +784,14 @@
(function(){ (function(){
var holdingShift = false; var holdingShift = false;
var updateShiftState = (pressed) => { const updateShiftState = (pressed) => {
if (pressed != holdingShift){ if (pressed != holdingShift){
holdingShift = pressed; holdingShift = pressed;
$("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear"); $("button[data-action='clear']").children("span").text(holdingShift ? "Restore" : "Clear");
} }
}; };
var resetActiveFocus = () => { const resetActiveFocus = () => {
document.activeElement.blur(); document.activeElement.blur();
}; };
@@ -775,7 +822,7 @@
// Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards. // Block: Swap shift key functionality for selecting accounts, and refocus the textbox afterwards.
// //
onAppReady.push(function(){ onAppReady.push(function(){
var onAccountClick = function(e){ const onAccountClick = function(e){
if ($TDX.switchAccountSelectors){ if ($TDX.switchAccountSelectors){
e.shiftKey = !e.shiftKey; e.shiftKey = !e.shiftKey;
} }
@@ -796,23 +843,63 @@
}); });
// //
// Block: Make middle click on tweet reply icon open the compose drawer. // Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
// //
app.delegate(".js-reply-action", "mousedown", function(e){ app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){
if (e.which === 2){ return if e.which !== 2;
if ($("[data-drawer='compose']").hasClass("is-hidden")){
$(document).trigger("uiDrawerShowDrawer", { let column = TD.controller.columnManager.get($(this).closest("section.js-column").attr("data-column"));
drawer: "compose", return if !column;
withAnimation: true
let ele = $(this).closest("article");
let tweet = column.findChirp(ele.attr("data-tweet-id")) || column.findChirp(ele.attr("data-key"));
return if !tweet;
switch($(this).attr("rel")){
case "reply":
let main = tweet.getMainTweet();
$(document).trigger("uiDockedComposeTweet", {
type: "reply",
from: [ tweet.account.getKey() ],
inReplyTo: {
id: tweet.id,
htmlText: main.htmlText,
user: {
screenName: main.user.screenName,
name: main.user.name,
profileImageURL: main.user.profileImageURL
}
},
mentions: tweet.getReplyUsers(),
element: ele
}); });
window.setTimeout(() => $(this).trigger("click"), 1); break;
}
e.preventDefault(); case "favorite":
e.stopPropagation(); $(document).trigger("uiShowFavoriteFromOptions", { tweet });
e.stopImmediatePropagation(); break;
case "retweet":
TD.controller.stats.quoteTweet();
$(document).trigger("uiComposeTweet", {
type: "tweet",
from: [ tweet.account.getKey() ],
quotedTweet: tweet.getMainTweet(),
element: ele // triggers reply-account plugin
});
break;
default:
return;
} }
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
}); });
// //
@@ -826,7 +913,7 @@
// Block: Inject custom CSS and layout into the page. // Block: Inject custom CSS and layout into the page.
// //
(function(){ (function(){
var createStyle = function(id, styles){ const createStyle = function(id, styles){
let ele = document.createElement("style"); let ele = document.createElement("style");
ele.id = id; ele.id = id;
ele.innerText = styles; ele.innerText = styles;
@@ -870,26 +957,34 @@
// Block: Setup video player hooks. // Block: Setup video player hooks.
// //
(function(){ (function(){
var playVideo = function(url){ window.TDGF_playVideo = function(url, username){
$('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){ $('<div id="td-video-player-overlay" class="ovl" style="display:block"></div>').on("click contextmenu", function(){
$TD.playVideo(null); $TD.playVideo(null, null);
}).appendTo(app); }).appendTo(app);
$TD.playVideo(url); $TD.playVideo(url, username || null);
}; };
var getVideoTweetLink = function(obj){ const getGifLink = function(ele){
return ele.attr("src") || ele.children("source[video-src]").first().attr("video-src");
};
const getVideoTweetLink = function(obj){
let parent = obj.closest(".js-tweet").first(); let parent = obj.closest(".js-tweet").first();
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first(); let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
return link.attr("href"); return link.attr("href");
} }
const getUsername = function(tweet){
return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName;
}
app.delegate(".js-gif-play", { app.delegate(".js-gif-play", {
click: function(e){ click: function(e){
let src = !e.ctrlKey && $(this).closest(".js-media-gif-container").find("video").attr("src"); let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
if (src){ if (src){
playVideo(src); window.TDGF_playVideo(src, getUsername(highlightedTweetObj));
} }
else{ else{
$TD.openBrowser(getVideoTweetLink($(this))); $TD.openBrowser(getVideoTweetLink($(this)));
@@ -925,7 +1020,7 @@
let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId); let media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
if (media && media.isVideo && media.service === "twitter"){ if (media && media.isVideo && media.service === "twitter"){
playVideo(media.chooseVideoVariant().url); window.TDGF_playVideo(media.chooseVideoVariant().url, getUsername(this.chirp));
cancelModal = true; cancelModal = true;
} }
}); });
@@ -1006,6 +1101,69 @@
} }
}); });
//
// Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally.
//
onAppReady.push(function(){
const openSearchExternally = function(event, input){
$TD.openBrowser("https://twitter.com/search/?q="+encodeURIComponent(input.val() || ""));
event.preventDefault();
event.stopPropagation();
input.val("").blur();
app.click(); // unfocus everything
};
$(".js-app-search-input").keydown(function(e){
(e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter
});
$(".js-perform-search").on("click auxclick", function(e){
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible"));
}).each(function(){
window.TDGF_prioritizeNewestEvent($(this)[0], "click");
});
$("[data-action='show-search']").on("click auxclick", function(e){
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $());
});
});
//
// Block: Override language used for translations.
//
if (ensurePropertyExists(TD, "languages", "getSystemLanguageCode")){
const prevFunc = TD.languages.getSystemLanguageCode;
TD.languages.getSystemLanguageCode = function(returnShortCode){
return returnShortCode ? ($TDX.translationTarget || "en") : prevFunc.apply(this, arguments);
};
}
//
// Block: Setup global function to refresh all columns.
//
window.TDGF_reloadColumns = function(){
Object.values(TD.controller.columnManager.getAll()).forEach(column => column.reloadTweets());
};
//
// Block: Allow applying ROT13 to input selection.
//
window.TDGF_applyROT13 = function(){
let ele = document.activeElement;
return if !ele || !ele.value;
let selection = ele.value.substring(ele.selectionStart, ele.selectionEnd);
return if !selection;
document.execCommand("insertText", false, selection.replace(/[a-zA-Z]/g, function(chr){
let code = chr.charCodeAt(0);
let start = code <= 90 ? 65 : 97;
return String.fromCharCode(start+(code-start+13)%26);
}));
};
// //
// 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.
// //
@@ -1021,7 +1179,7 @@
// Block: Fix DM notifications not showing if the conversation is open. // Block: Fix DM notifications not showing if the conversation is open.
// //
if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "getUnreadChirps")){ if (ensurePropertyExists(TD, "services", "TwitterConversation", "prototype", "getUnreadChirps")){
var prevFunc = TD.services.TwitterConversation.prototype.getUnreadChirps; const prevFunc = TD.services.TwitterConversation.prototype.getUnreadChirps;
TD.services.TwitterConversation.prototype.getUnreadChirps = function(e){ TD.services.TwitterConversation.prototype.getUnreadChirps = function(e){
return (e && e.sortIndex && !e.id && !this.notificationsDisabled) return (e && e.sortIndex && !e.id && !this.notificationsDisabled)
@@ -1082,35 +1240,6 @@
}); });
}; };
(function(){
var lastActivity = Date.now();
$(document).click(function(e){
lastActivity = Date.now();
});
window.TDGF_tryRunCleanup = function(){
// no recent activity
return false if Date.now()-lastActivity < 15e3;
// no modals are visible
return false if $(".js-modal").is(":visible") || !$(".js-modals-container").is(":empty");
// all columns are in a default state
return false if $("section.js-column").is(".is-shifted-1,.is-shifted-2");
// all textareas are empty
return false if Array.prototype.some.call(document.getElementsByTagName("textarea"), ele => ele.value.length > 0);
// all columns are scrolled to top
return false if Array.prototype.some.call(document.getElementsByClassName("js-column-scroller"), ele => ele.scrollTop > 0);
// cleanup
window.TDGF_reload();
return true;
};
})();
if (window.TD_SESSION && window.TD_SESSION.gc){ if (window.TD_SESSION && window.TD_SESSION.gc){
var state; var state;
@@ -1163,15 +1292,24 @@
}); });
} }
//
// Block: Disable default TweetDeck update notification.
//
onAppReady.push(function(){
let events = $._data(document, "events");
delete events["uiSuggestRefreshToggle"];
});
// //
// Block: Disable TweetDeck metrics. // Block: Disable TweetDeck metrics.
// //
if (ensurePropertyExists(TD, "metrics")){ if (ensurePropertyExists(TD, "metrics")){
TD.metrics.inflate = function(){}; const noop = function(){};
TD.metrics.inflateMetricTriple = function(){}; TD.metrics.inflate = noop;
TD.metrics.log = function(){}; TD.metrics.inflateMetricTriple = noop;
TD.metrics.makeKey = function(){}; TD.metrics.log = noop;
TD.metrics.send = function(){}; TD.metrics.makeKey = noop;
TD.metrics.send = noop;
} }
onAppReady.push(function(){ onAppReady.push(function(){

View File

@@ -8,8 +8,8 @@
#td-introduction-modal .mdl { #td-introduction-modal .mdl {
width: 90%; width: 90%;
max-width: 800px; max-width: 830px;
height: 372px; height: 328px;
} }
#td-introduction-modal .mdl-header-title { #td-introduction-modal .mdl-header-title {
@@ -31,6 +31,19 @@
text-shadow: 0 0 #000; text-shadow: 0 0 #000;
} }
#td-introduction-modal .main-menu {
float: left;
width: 187px;
height: 124px;
margin-right: 12px;
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.33);
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALsAAAB8CAMAAAAGozFUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAACZUExURQAAAAAANgAAYQA2YQA2iABhYQBhrDYAADYANjYAYTY2ADY2NjY2YTY2iDZhrDaIiDaIz2EAAGEANmEAYWE2NmE2iGFhNmFhYWGs8og2AIg2Nog2YYiIYYisiIjPrIjPz4jP8qxhAKxhNqzPiKzyrKzy8szMzM+INs/PiM/yrM/yz8/y8tfX1/Dw8PKsYfLPiPLyrPLyz/Ly8nG9GIQAAARDSURBVHja7ZyPd9IwEMdDxbI5cemcWJjbWrWrDi2Q//+PM7m7pOGHfcORlD6v7w1ok2wfwqXkvrs78TTcQzypoR6DZ//dcTA7szM7s8dgX2dCiJxGrG+Kvd+C1w619M+uoZqkGiy7AxvivI+XajURSWVO9AtjQ6UQqTIn18R+PxFSP82SCnuUOYxdfdjMzSmNn9mPMJa9p/QWpH5cZ5rjgqbanNQjOMnGS315nWl+7NGkqrmSqpb6hRuvm+PO++qygtkWKZ2YOW2EGBWr6dK3mTKHmcYeuu3r/fR5UawmUrXjY9tMLRFItez6abPYYd/MffbN4uHT89033UPVInfje1ir+Glbm7msGjKR1mbg/UFv7KFqYzGfwUhq6cbHZle1Jp3QZ65fjMwcize3hbEcu1ZnZkVCb+wBq8L86D4JGl1sm+E9AbMzO7MzO7MzO7Mze586QQqdcav7ggO3juMfF8Whlsj+6hXuzx17x0aWmswO/nBLbPaZ8RqatzdHsO/36In94WOltAOnXSZwoR7A+Qa3H5q/CEH+aA5+OfaQ6ASCYGAuZq3nAi4KjML2oH6T9qxX05+W3T5pt5+WQ5NUpA1s99jMpZt05zpiPxzVpOF1gukSFACPDN3+1p0l/3qbHf1rVBQO9gMFISz7ZnE/Xe6wX3oqXzc7KgoH+4GCEFoXA8VL20CuGprCTG6xkzZwyGZIUTjUDxSEwOybu4regzC3mxLXqm8zVhsoYUXai2aF5qQolL7KQB1QQeDvVWZndmZndmZn9nNn/3WuB9sMszM7szM7sw+WXXvEQgYTRYOyNxA9IEMJiyHZjSijWi1pUOxIDcqWiao6uSgalN0E70BYT7YncJ1CFI0074Uvg55KFI1i715U1SlF0bD3maRSGH4nfbn0VKJo+Pt77qKqTi2KRvlePZObI7PzXozZmZ3ZmZ3Zmf1/YjdO3qjY3xH0rBW8PN9jvDyv3cyR+R7DZvdCNq7pQk8iwfE243l7NUTR9CYSHLNWk8qPRsKcJu9CdJHgmHlXqos9vkhwPLsNuWptpi+R4Hh2G3KF+Vgte3yR4FXfq620Orw9QTleDpLd3M6Tvqed95HMzuzMzuzMzuw9sJep6v7XQSnEXsyBd0AZmN3WVzrAL2Rfvb8t/vK3OgukKM/ddelpkdlraVyLV7LvbZujsLuyG1CkxcURtGlalsQVbaHELfP43YYfeP9WRmGBfPWw7Bpc47dFWnaTsBy7K9ri6r3Q4w47BrSsbx7neeh5NwZTp7ZIy34ik2P3iraYxK22aguyb+c46RfvZGibgZsElfzRc9fB7oq2uJysHXvfZs+u0tDspl4RhqBgOAHGEXhpWu3Ks0VbqF6Lq9piC7vQIGszRSkDs+MfqFMbTkBJWF6almO3RVsocQsfS0yihOxXGgQpW2D4/+728p6A2Zmd2Zmd2c+SfaCHZh/u8QdspaeBZ15I7gAAAABJRU5ErkJggg==);
}
#td-introduction-modal .main-menu + p {
margin-top: 15px;
}
#td-introduction-modal footer { #td-introduction-modal footer {
padding: 10px 0; padding: 10px 0;
} }
@@ -65,10 +78,10 @@
<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><strong>Right-click anywhere</strong> or click <strong>Settings TweetDuck</strong> in the left panel to open the main menu, where you can access the <strong>Options</strong>, <strong>Plugins</strong>, and more.</p> <p><a id="td-introduction-follow" href="#">Follow @TryTweetDuck</a> for latest news and updates about the app.</p>
<p>You can also right-click links, media, tweets, notifications, etc. to access their context menu.</p> <div class="main-menu"></div>
<p>If you're using TweetDuck for the first time, check out the <strong>guide</strong> that answers common questions and showcases important features. You can open the main menu, select <strong>About TweetDuck</strong>, and click the help button to view the guide later.</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>Before you continue, please consider helping development by allowing TweetDuck to send anonymous data in the future. You can always disable it in <strong>Options Feedback</strong>.</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>
</div> </div>
<footer class="txt-right"> <footer class="txt-right">
<div class="anondata"> <div class="anondata">
@@ -83,6 +96,30 @@
</div> </div>
</div>`).appendTo(".js-app"); </div>`).appendTo(".js-app");
let tdUser = null;
let loadTweetDuckUser = (onSuccess, onError) => {
if (tdUser !== null){
onSuccess(tdUser);
}
else{
TD.controller.clients.getPreferredClient().getUsersByIds([ "731137856052269056" ], users => onSuccess(users[0]), onError);
}
};
loadTweetDuckUser(user => tdUser = user);
ele.find("#td-introduction-follow").click(function(){
loadTweetDuckUser(user => {
$(document).trigger("uiShowFollowFromOptions", { userToFollow: user });
$(".js-modals-container").find("header a[rel='user']").each(function(){
this.outerHTML = this.innerText;
});
}, () => {
alert("An error occurred when retrieving the account information.");
});
});
ele.find("button, a.mdl-dismiss").click(function(){ ele.find("button, a.mdl-dismiss").click(function(){
let showGuide = $(this)[0].hasAttribute("data-guide"); let showGuide = $(this)[0].hasAttribute("data-guide");
let allowDataCollection = $("#td-anonymous-data").is(":checked"); let allowDataCollection = $("#td-anonymous-data").is(":checked");

View File

@@ -52,7 +52,7 @@
return if !url; return if !url;
var text = me.textContent; var text = me.textContent;
return if text.charCodeAt(text.length-1) !== 8230; // horizontal ellipsis return if text.charCodeAt(text.length-1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
if ($TDX.expandLinksOnHover){ if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){ tooltipTimer = window.setTimeout(function(){

View File

@@ -3,7 +3,7 @@
<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-small">0s</a> <a target="_blank" rel="url" href="https://twitter.com/chylexmc" class="txt-size-variable--12">0s</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/chylexmc" class="account-link link-complex block">
<div class="obj-left item-img tweet-img"> <div class="obj-left item-img tweet-img">

View File

@@ -87,14 +87,14 @@
}; };
// //
// Block: Setup global function to change plugin state. // Block: Setup a function to change plugin state.
// //
window.TDPF_setPluginState = function(identifier, enable){ window.TDPF_setPluginState = function(identifier, enable){
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable); window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier), enable);
}; };
// //
// Block: Setup global function to reload the page. // Block: Setup a function to reload the page.
// //
window.TDPF_requestReload = function(){ window.TDPF_requestReload = function(){
if (!isReloading){ if (!isReloading){
@@ -102,4 +102,11 @@
isReloading = true; isReloading = true;
} }
}; };
//
// Block: Setup bridges to global functions.
//
window.TDPF_playVideo = window.TDGF_playVideo;
window.TDPF_reloadColumns = window.TDGF_reloadColumns;
window.TDPF_prioritizeNewestEvent = window.TDGF_prioritizeNewestEvent;
})(); })();

View File

@@ -3,23 +3,23 @@
/***********************/ /***********************/
.scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar { .scroll-styled-v::-webkit-scrollbar-thumb, .scroll-styled-h::-webkit-scrollbar-thumb, .antiscroll-scrollbar {
border-radius: 0; border-radius: 0 !important;
} }
.antiscroll-scrollbar-vertical { .antiscroll-scrollbar-vertical {
margin-top: 0; margin-top: 0 !important;
} }
.antiscroll-scrollbar-horizontal { .antiscroll-scrollbar-horizontal {
margin-left: 0; margin-left: 0 !important;
} }
.scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar { .scroll-styled-v:not(.antiscroll-inner)::-webkit-scrollbar {
width: 8px; width: 8px !important;
} }
.scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar { .scroll-styled-h:not(.antiscroll-inner)::-webkit-scrollbar {
height: 8px; height: 8px !important;
} }
.app-columns-container::-webkit-scrollbar { .app-columns-container::-webkit-scrollbar {
@@ -27,7 +27,7 @@
} }
.app-columns-container::-webkit-scrollbar-track { .app-columns-container::-webkit-scrollbar-track {
border-left: 0; border-left: 0 !important;
} }
.app-columns-container { .app-columns-container {
@@ -38,11 +38,19 @@
/* Square-ify stuff */ /* Square-ify stuff */
/********************/ /********************/
.btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .media-item, .media-preview, .tooltip-inner { .btn, .mdl, .mdl-content, .app-search-fake, .app-search-input, .popover, .lst-modal, .tooltip-inner {
border-radius: 1px !important; border-radius: 1px !important;
} }
.compose-text-container, .dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select, .prf-header, .accs li, .accs img { .media-item, .media-preview, .media-image, .js-media-added .br--4 {
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 {
border-radius: 0 !important;
}
.dropdown-menu, .list-item-last, .quoted-tweet, .input-group-button, input, textarea, select, .prf-header, .accs li, .accs img {
border-radius: 0 !important; border-radius: 0 !important;
} }
@@ -79,20 +87,25 @@
} }
.app-header:not(.is-condensed) .nav-user-info { .app-header:not(.is-condensed) .nav-user-info {
padding: 0 5px; padding: 0 5px !important;
} }
/****************************************/ /****************************************/
/* Tweak notification layout and design */ /* Tweak notification layout and design */
/****************************************/ /****************************************/
.tweet-context .nbfc {
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.activity-header { .activity-header {
align-items: center !important; align-items: center !important;
margin-bottom: 4px; margin-bottom: 4px !important;
} }
.activity-header .tweet-timestamp { .activity-header .tweet-timestamp {
line-height: unset; line-height: unset !important;
} }
.account-bio.padding-t--5 { .account-bio.padding-t--5 {
@@ -118,17 +131,12 @@ html[data-td-theme='dark'] .stream-item:not(:hover) .js-user-actions-menu {
a[data-full-url] { a[data-full-url] {
/* break long urls */ /* break long urls */
word-break: break-all; word-break: break-all !important;
}
.character-count-compose {
/* fix strangely wide character count element */
width: 40px !important;
} }
.is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot { .is-video a:not([href*='youtu']) .icon-bg-dot, .is-gif .icon-bg-dot {
/* change play icon on mp4s */ /* change play icon on mp4s */
color: #9f51cf; color: #9f51cf !important;
} }
/***************************************/ /***************************************/
@@ -144,7 +152,7 @@ a[data-full-url] {
} }
.js-docked-compose footer { .js-docked-compose footer {
display: none; display: none !important;
} }
.compose-content { .compose-content {
@@ -157,23 +165,35 @@ a[data-full-url] {
.account-inline .username { .account-inline .username {
/* move usernames a bit higher */ /* move usernames a bit higher */
vertical-align: 10%; vertical-align: 10% !important;
} }
.txt-base-smallest .sprite-verified-mini { .account-inline .position-rel + .username {
/* except for follow notifications because consistency eh */
vertical-align: -10% !important;
}
html[data-td-font='smallest'] .sprite-verified-mini {
/* fix cut off badge when zoomed in */ /* fix cut off badge when zoomed in */
width: 13px !important; width: 13px !important;
height: 13px !important; height: 13px !important;
background-position: -223px -99px !important; background-position: -223px -99px !important;
} }
.txt-base-smallest .badge-verified:before { html[data-td-font='smallest'] .badge-verified:before {
/* fix cut off badge in notifications */ /* fix cut off badge in notifications */
width: 13px !important; width: 13px !important;
height: 13px !important; height: 13px !important;
background-position: -223px -98px !important; background-position: -223px -98px !important;
} }
html[data-td-font='smallest'] .tweet-detail-wrapper .badge-verified:before {
/* fix cut off badge in detail view */
width: 13px !important;
height: 14px !important;
background-position: -223px -97px !important;
}
.accs-header { .accs-header {
/* fix retweet account selector heading */ /* fix retweet account selector heading */
padding-left: 0 !important; padding-left: 0 !important;
@@ -181,7 +201,7 @@ a[data-full-url] {
.keyboard-shortcut-list { .keyboard-shortcut-list {
/* fix keyboard navigation alignment */ /* fix keyboard navigation alignment */
vertical-align: top; vertical-align: top !important;
} }
.tweet-detail-wrapper .js-media-gif-container { .tweet-detail-wrapper .js-media-gif-container {
@@ -189,20 +209,31 @@ a[data-full-url] {
cursor: pointer; cursor: pointer;
} }
/********************************************************************/ /***************************************************************/
/* Fix glaring visual issues because twitter is fucking incompetent */ /* Fix glaring visual issues that twitter hasn't fixed yet smh */
/********************************************************************/ /***************************************************************/
.column-nav-link .attribution { .column-nav-link .attribution {
/* fix cut off account names */ /* fix cut off account names */
position: absolute; position: absolute !important;
} }
.js-docked-compose .js-drawer-close { #tduck .js-docked-compose .js-drawer-close {
/* fix close drawer button position */ /* fix close drawer button position */
margin: 20px 0 0 !important; margin: 20px 0 0 !important;
} }
.compose-reply-tweet-remove {
/* fix close reply button position; this would make sense if the reply account name became full size, but it still cuts off where the button used to be */
top: 3px !important;
right: -2px !important;
}
#account-safeguard-checkbox, #inline-account-safeguard-checkbox {
/* fix "Ready to Tweet/send" alignment with the checkbox */
vertical-align: -15% !important;
}
/************************************************/ /************************************************/
/* Fix tooltips in navigation and column header */ /* Fix tooltips in navigation and column header */
/************************************************/ /************************************************/
@@ -212,7 +243,7 @@ a[data-full-url] {
} }
.js-column-header .column-header-link { .js-column-header .column-header-link {
padding: 0; padding: 0 !important;
} }
.js-column-header .column-header-link .icon { .js-column-header .column-header-link .icon {
@@ -231,9 +262,28 @@ a[data-full-url] {
} }
.column:not(.is-options-open) .column-header { .column:not(.is-options-open) .column-header {
border-bottom: none; border-bottom: none !important;
} }
.is-options-open .column-type-icon { .is-options-open .column-type-icon {
bottom: 27px; bottom: 27px !important;
}
/********************************************/
/* Fix cut off usernames in Messages column */
/********************************************/
.column-type-message.is-shifted-1 .column-title-container {
height: 100%;
border-bottom-color: transparent;
}
#tduck .column-type-message.is-shifted-1 .column-title-items {
height: 100%;
margin-left: 4px !important;
padding-top: 1px;
}
.column-type-message.is-shifted-1 .username {
vertical-align: bottom;
} }

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