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

Compare commits

...

47 Commits
1.6 ... 1.6.3

Author SHA1 Message Date
92af85d3bb Release 1.6.3 2017-01-28 18:49:23 +01:00
7635af5730 Add an AppName suffix for portable and update installers 2017-01-28 18:49:14 +01:00
a838e89695 Fix custom sound notification textbox not setting color when the control is created 2017-01-28 18:12:30 +01:00
b22289a8b9 Work around Alt freezing the app since W10 Anniversary Update
Get fucked, Microsoft
2017-01-28 17:44:30 +01:00
45b3ff52c6 Tweak FormBrowser.ShowChildForm to use VisibleChanged instead of Shown event for reliability 2017-01-28 01:08:31 +01:00
4464991f4c Prevent automatic Settings tab selection from triggering autoclick in Notification tab 2017-01-28 01:07:57 +01:00
b0d2f77583 Merge pull request #101 from chylex/ipc
Replace WCF with native chromium IPC
2017-01-28 00:01:22 +01:00
b211a4405d Set CefSharpSettings.WcfEnabled to false 2017-01-27 23:59:01 +01:00
8823016d2c Make custom sound notification textbox font red when the file doesn't exist 2017-01-27 23:56:51 +01:00
859fdc7ec1 Rewrite custom sound notification to show an error message on failure instead of hiding it 2017-01-27 23:56:00 +01:00
028d5ed01f Improve debug script for easier extendibility, add sound notification simulation 2017-01-27 21:48:57 +01:00
5fd5a2a436 Use and test RegisterAsyncJsObject in FormBrowser 2017-01-27 18:51:14 +01:00
79a7e7470c Use and test RegisterAsyncJsObject in FormNotification 2017-01-27 17:00:09 +01:00
9ecef78aed Fix DismissedUpdate not being set after toggling updates 2017-01-27 16:21:36 +01:00
65a837a6e1 Move TweetDeckBridge properties to a separate JS object 2017-01-27 16:13:17 +01:00
6e4db4acea Rewrite custom CSS injection and automatically inject it while typing 2017-01-26 15:35:40 +01:00
26fb977d05 Remove unnecessary properties from TweetDeckBridge 2017-01-26 06:51:51 +01:00
b42cd1c048 Tweak screenshot notification script (minor edit) 2017-01-26 06:46:19 +01:00
467f7cd12f Rewrite update system to use RegisterAsyncJsObject 2017-01-26 06:41:20 +01:00
66699ce9df Change update progress form to show kB instead of MB 2017-01-26 06:39:46 +01:00
cf7d903932 Move updater event args to a separate namespace 2017-01-26 04:09:04 +01:00
a7ab67925c Allow moving the notification window when holding Alt in debug builds 2017-01-23 01:13:15 +01:00
a474ba4260 Fix incorrect cursor when hovering over quoted tweet in notification
Closes #97
2017-01-23 01:00:16 +01:00
09e5636e86 Remove unused 'using' statement 2017-01-23 00:59:19 +01:00
2295a875be Fix 'Copy' context menu item (separator in wrong place in browser, missing in notification) 2017-01-23 00:53:15 +01:00
82a2455afc Release 1.6.2 2017-01-23 00:33:06 +01:00
268de676ee Add NativeMethods.GetIdleSeconds for idle time detection 2017-01-22 16:00:54 +01:00
8fe26c07f1 Preserve plaintext when stripping HTML styles from clipboard text 2017-01-17 18:29:09 +01:00
da3921b1ca Add safeguards for clipboard update methods
Closes #91
2017-01-17 18:19:39 +01:00
4dd2e787d1 Remove unnecessary IsSystemSupported check in UpdateHandler 2017-01-17 02:48:17 +01:00
ce005ae6c2 Add unit tests for CombinedFileStream 2017-01-17 02:33:47 +01:00
1513f46a11 Add a safety net to CombinedFileStream.Entry.WriteToFile with createDirectory 2017-01-17 02:27:03 +01:00
7543eeb0f4 Add more methods to TestUtils and fix cleanup code not running 2017-01-17 01:38:55 +01:00
873242120c Add a TestUtils class for easy file manipulation and cleanup in unit tests 2017-01-17 01:20:12 +01:00
98f8095a65 Add unit tests for CommandLineArgsParser 2017-01-16 22:46:01 +01:00
785571a550 Add unit tests for BrowserUtils and CommandLineArgs 2017-01-16 22:06:04 +01:00
0c4bd4044e Add ReSharper code coverage settings and cleanup the test project 2017-01-16 20:56:36 +01:00
0319543dce Add a unit test project 2017-01-16 19:36:29 +01:00
82d70b2d7f Stealthfix a bug with CommandLineArgs.ToString causing an exception if there are no args 2017-01-10 21:58:17 +01:00
62d18e010a Release 1.6.1 2017-01-10 21:24:54 +01:00
fc77b85083 Remove HTML styles after copying selected text to clipboard 2017-01-08 16:36:49 +01:00
50a8893f4f Add an option to disable screenshot window border 2017-01-08 02:47:47 +01:00
9252b3040e Fix screenshot functionality broken by previous refactoring 2017-01-08 02:26:09 +01:00
d5141ed020 Redo OnNotificationReady call to use LoadingStateChanged with a delay 2017-01-08 02:16:40 +01:00
7ff9e23283 Remove legacy notification loading option 2017-01-08 01:33:48 +01:00
89854d527c Fix notification position config after changing namespace and remove TweetNotification.Duration 2017-01-03 18:43:36 +01:00
6ff0cad2a8 Pre-release 1.6 2017-01-03 17:45:52 +01:00
50 changed files with 1115 additions and 310 deletions

View File

@@ -12,9 +12,9 @@ using TweetDck.Plugins;
namespace TweetDck.Configuration{
[Serializable]
sealed class UserConfig{
private static readonly IFormatter Formatter = new BinaryFormatter();
private static readonly IFormatter Formatter = new BinaryFormatter{ Binder = new CustomBinder() };
private const int CurrentFileVersion = 5;
private const int CurrentFileVersion = 6;
// START OF CONFIGURATION
@@ -22,16 +22,15 @@ namespace TweetDck.Configuration{
public bool DisplayNotificationTimer { get; set; }
public bool NotificationTimerCountDown { get; set; }
public TweetNotification.Duration NotificationDuration { get; set; }
public TweetNotification.Position NotificationPosition { get; set; }
public Point CustomNotificationPosition { get; set; }
public int NotificationEdgeDistance { get; set; }
public int NotificationDisplay { get; set; }
public int NotificationDurationValue { get; set; }
public bool NotificationLegacyLoad { get; set; }
public bool EnableSpellCheck { get; set; }
public bool ExpandLinksOnHover { get; set; }
public bool ShowScreenshotBorder { get; set; }
public bool EnableTrayHighlight { get; set; }
public bool EnableUpdateCheck { get; set; }
@@ -68,7 +67,7 @@ namespace TweetDck.Configuration{
public string NotificationSoundPath{
get{
return !string.IsNullOrEmpty(notificationSoundPath) && File.Exists(notificationSoundPath) ? notificationSoundPath : string.Empty;
return string.IsNullOrEmpty(notificationSoundPath) ? string.Empty : notificationSoundPath;
}
set{
@@ -113,13 +112,13 @@ namespace TweetDck.Configuration{
BrowserWindow = new WindowState();
DisplayNotificationTimer = true;
NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = ControlExtensions.InvisibleLocation;
NotificationEdgeDistance = 8;
NotificationDurationValue = 25;
EnableUpdateCheck = true;
ExpandLinksOnHover = true;
ShowScreenshotBorder = true;
EnableTrayHighlight = true;
Plugins = new PluginConfig();
PluginsWindow = new WindowState();
@@ -154,14 +153,7 @@ namespace TweetDck.Configuration{
if (fileVersion == 3){
EnableTrayHighlight = true;
switch(NotificationDuration){
case TweetNotification.Duration.Short: NotificationDurationValue = 15; break;
case TweetNotification.Duration.Medium: NotificationDurationValue = 25; break;
case TweetNotification.Duration.Long: NotificationDurationValue = 35; break;
case TweetNotification.Duration.VeryLong: NotificationDurationValue = 45; break;
}
NotificationDurationValue = 25;
++fileVersion;
}
@@ -171,6 +163,11 @@ namespace TweetDck.Configuration{
++fileVersion;
}
if (fileVersion == 5){
ShowScreenshotBorder = true;
++fileVersion;
}
// update the version
fileVersion = CurrentFileVersion;
Save();
@@ -241,5 +238,15 @@ namespace TweetDck.Configuration{
public static string GetBackupFile(string file){
return file+".bak";
}
private sealed class CustomBinder : SerializationBinder{
public override Type BindToType(string assemblyName, string typeName){
if (typeName == "TweetDck.Core.Handling.TweetNotification+Position"){
return typeof(TweetNotification.Position);
}
return null;
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Text;
namespace TweetDck.Core.Bridge{
static class PropertyBridge{
[Flags]
public enum Properties{
ExpandLinksOnHover = 1,
MuteNotifications = 2,
HasCustomNotificationSound = 4,
All = ExpandLinksOnHover | MuteNotifications | HasCustomNotificationSound
}
public static string GenerateScript(Properties properties = Properties.All){
StringBuilder build = new StringBuilder();
build.Append("(function(c){");
if (properties.HasFlag(Properties.ExpandLinksOnHover)){
build.Append("c.expandLinksOnHover=").Append(Program.UserConfig.ExpandLinksOnHover ? "true;" : "false;");
}
if (properties.HasFlag(Properties.MuteNotifications)){
build.Append("c.muteNotifications=").Append(Program.UserConfig.MuteNotifications ? "true;" : "false;");
}
if (properties.HasFlag(Properties.HasCustomNotificationSound)){
build.Append("c.hasCustomNotificationSound=").Append(Program.UserConfig.NotificationSoundPath.Length > 0 ? "true;" : "false;");
}
build.Append("})(window.$TDX=window.$TDX||{})");
return build.ToString();
}
}
}

View File

@@ -21,48 +21,6 @@ namespace TweetDck.Core.Bridge{
private readonly FormBrowser form;
private readonly FormNotification notification;
public string BrandName{
get{
return Program.BrandName;
}
}
public string VersionTag{
get{
return Program.VersionTag;
}
}
public bool MuteNotifications{
get{
return Program.UserConfig.MuteNotifications;
}
}
public bool HasCustomNotificationSound{
get{
return !string.IsNullOrEmpty(Program.UserConfig.NotificationSoundPath);
}
}
public bool ExpandLinksOnHover{
get{
return Program.UserConfig.ExpandLinksOnHover;
}
}
public bool HasCustomBrowserCSS{
get{
return !string.IsNullOrEmpty(Program.UserConfig.CustomBrowserCSS);
}
}
public string CustomBrowserCSS{
get{
return Program.UserConfig.CustomBrowserCSS;
}
}
public TweetDeckBridge(FormBrowser form, FormNotification notification){
this.form = form;
this.notification = notification;
@@ -117,12 +75,6 @@ namespace TweetDck.Core.Bridge{
});
}
public void OnNotificationReady(){
if (!Program.UserConfig.NotificationLegacyLoad){
notification.InvokeSafe(notification.OnNotificationReady);
}
}
public void DisplayTooltip(string text, bool showInNotification){
if (showInNotification){
notification.InvokeSafe(() => notification.DisplayTooltip(text));
@@ -172,6 +124,10 @@ namespace TweetDck.Core.Bridge{
form.InvokeSafe(() => form.OnTweetScreenshotReady(html, width, height));
}
public void FixClipboard(){
form.InvokeSafe(WindowsUtils.ClipboardStripHtmlStyles);
}
public void OpenBrowser(string url){
BrowserUtils.OpenExternalBrowser(url);
}

View File

@@ -17,6 +17,8 @@ using System.Media;
using TweetDck.Core.Bridge;
using TweetDck.Core.Notification;
using TweetDck.Core.Notification.Screenshot;
using TweetDck.Updates.Events;
using System.IO;
namespace TweetDck.Core{
sealed partial class FormBrowser : Form{
@@ -42,6 +44,7 @@ namespace TweetDck.Core{
private TweetScreenshotManager notificationScreenshotManager;
private SoundPlayer notificationSound;
private bool ignoreNotificationSoundError;
public FormBrowser(PluginManager pluginManager, UpdaterSettings updaterSettings){
InitializeComponent();
@@ -53,7 +56,11 @@ namespace TweetDck.Core{
this.plugins.PluginChangedState += plugins_PluginChangedState;
this.notification = CreateNotificationForm(NotificationFlags.AutoHide | NotificationFlags.TopMost);
#if DEBUG
this.notification.CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt;
#else
this.notification.CanMoveWindow = () => false;
#endif
this.notification.Show();
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
@@ -69,11 +76,13 @@ namespace TweetDck.Core{
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
this.browser.RegisterJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
Controls.Add(browser);
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
Disposed += (sender, args) => {
browser.Dispose();
@@ -92,13 +101,16 @@ namespace TweetDck.Core{
UpdateTrayIcon();
Config.MuteToggled += Config_MuteToggled;
this.updates = new UpdateHandler(browser, this, updaterSettings);
this.updates.UpdateAccepted += updates_UpdateAccepted;
this.updates.UpdateDismissed += updates_UpdateDismissed;
}
private void ShowChildForm(Form form){
form.VisibleChanged += (sender, args) => form.MoveToCenter(this);
form.Show(this);
form.Shown += (sender, args) => form.MoveToCenter(this);
}
public void ForceClose(){
@@ -135,7 +147,9 @@ namespace TweetDck.Core{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
UpdateProperties();
ScriptLoader.ExecuteFile(e.Frame, "code.js");
ReinjectCustomCSS(Config.CustomBrowserCSS);
#if DEBUG
ScriptLoader.ExecuteFile(e.Frame, "debug.js");
@@ -192,6 +206,10 @@ namespace TweetDck.Core{
}
}
private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Properties.MuteNotifications);
}
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
if (!isLoaded)return;
@@ -241,6 +259,11 @@ namespace TweetDck.Core{
}
}
private void updates_UpdateDismissed(object sender, UpdateDismissedEventArgs e){
Config.DismissedUpdate = e.VersionTag;
Config.Save();
}
protected override void WndProc(ref Message m){
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
trayIcon_ClickRestore(trayIcon, new EventArgs());
@@ -269,6 +292,16 @@ namespace TweetDck.Core{
notification.ResumeNotification();
}
// javascript calls
public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css == null ? string.Empty : css.Replace(Environment.NewLine, " "));
}
public void UpdateProperties(PropertyBridge.Properties properties = PropertyBridge.Properties.All){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(properties));
}
// callback handlers
public void OpenSettings(){
@@ -284,14 +317,18 @@ namespace TweetDck.Core{
currentFormSettings = null;
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
updates.Settings.DismissedUpdate = string.Empty;
Config.DismissedUpdate = string.Empty;
Config.Save();
updates.Check(false);
}
if (!Config.EnableTrayHighlight){
trayIcon.HasNotifications = false;
}
UpdateProperties(PropertyBridge.Properties.ExpandLinksOnHover | PropertyBridge.Properties.HasCustomNotificationSound);
};
ShowChildForm(currentFormSettings);
@@ -327,19 +364,49 @@ namespace TweetDck.Core{
}
public void PlayNotificationSound(){
if (string.IsNullOrEmpty(Config.NotificationSoundPath)){
if (Config.NotificationSoundPath.Length == 0){
return;
}
if (notificationSound == null){
notificationSound = new SoundPlayer();
notificationSound = new SoundPlayer{
LoadTimeout = 5000
};
}
if (notificationSound.SoundLocation != Config.NotificationSoundPath){
notificationSound.SoundLocation = Config.NotificationSoundPath;
ignoreNotificationSoundError = false;
}
notificationSound.Play();
try{
notificationSound.Play();
}catch(FileNotFoundException e){
OnNotificationSoundError("File not found: "+e.FileName);
}catch(InvalidOperationException){
OnNotificationSoundError("File is not a valid sound file.");
}catch(TimeoutException){
OnNotificationSoundError("File took too long to load.");
}
}
private void OnNotificationSoundError(string message){
if (!ignoreNotificationSoundError){
ignoreNotificationSoundError = true;
using(FormMessage form = new FormMessage("Notification Sound Error", "Could not play custom notification sound."+Environment.NewLine+message, MessageBoxIcon.Error)){
form.AddButton("Ignore");
Button btnOpenSettings = form.AddButton("Open Settings");
btnOpenSettings.Width += 16;
btnOpenSettings.Location = new Point(btnOpenSettings.Location.X-16, btnOpenSettings.Location.Y);
if (form.ShowDialog() == DialogResult.OK && form.ClickedButton == btnOpenSettings){
OpenSettings();
currentFormSettings.SelectTab(FormSettings.TabIndexNotification);
}
}
}
}
public void OnTweetScreenshotReady(string html, int width, int height){

View File

@@ -30,6 +30,7 @@ namespace TweetDck.Core {
this.timerProgress = new System.Windows.Forms.Timer(this.components);
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.timerDisplayDelay = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// panelBrowser
@@ -61,6 +62,11 @@ namespace TweetDck.Core {
this.progressBarTimer.Size = new System.Drawing.Size(284, 4);
this.progressBarTimer.TabIndex = 1;
//
// timerDisplayDelay
//
this.timerDisplayDelay.Interval = 17;
this.timerDisplayDelay.Tick += new System.EventHandler(this.timerDisplayDelay_Tick);
//
// FormNotification
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -88,5 +94,6 @@ namespace TweetDck.Core {
private Controls.FlatProgressBar progressBarTimer;
private System.Windows.Forms.Timer timerProgress;
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Timer timerDisplayDelay;
}
}

View File

@@ -85,7 +85,6 @@ namespace TweetDck.Core{
public string CurrentQuotedTweetUrl { get; set; }
public EventHandler Initialized;
private bool isInitialized;
private int pauseCounter;
private bool pausedDuringNotification;
@@ -129,11 +128,12 @@ namespace TweetDck.Core{
#endif
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
if (!flags.HasFlag(NotificationFlags.DisableScripts)){
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
browser.RegisterJsObject("$TD", new TweetDeckBridge(owner, this));
browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(owner, this));
if (plugins != null){
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
@@ -194,6 +194,11 @@ namespace TweetDck.Core{
// event handlers
private void timerDisplayDelay_Tick(object sender, EventArgs e){
OnNotificationReady();
timerDisplayDelay.Stop();
}
private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
@@ -222,17 +227,18 @@ namespace TweetDck.Core{
}
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (!e.Frame.IsMain)return;
if (!isInitialized && !Program.UserConfig.NotificationLegacyLoad){
isInitialized = true;
if (Initialized != null){
Initialized(this, new EventArgs());
}
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading && browser.Address != "about:blank" && !flags.HasFlag(NotificationFlags.ManualDisplay)){
this.InvokeSafe(() => {
Visible = true; // ensures repaint before moving the window to a visible location
timerDisplayDelay.Start();
});
}
else if (notificationJS != null && browser.Address != "about:blank" && !flags.HasFlag(NotificationFlags.DisableScripts)){
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && notificationJS != null && browser.Address != "about:blank" && !flags.HasFlag(NotificationFlags.DisableScripts)){
e.Frame.ExecuteJavaScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Properties.ExpandLinksOnHover));
ScriptLoader.ExecuteScript(e.Frame, notificationJS, NotificationScriptIdentifier);
if (plugins != null && plugins.HasAnyPlugin(PluginEnvironment.Notification)){
@@ -282,7 +288,7 @@ namespace TweetDck.Core{
}
public void HideNotification(bool loadBlank){
if (loadBlank || Program.UserConfig.NotificationLegacyLoad){
if (loadBlank){
browser.LoadHtml("", "about:blank");
}
@@ -294,12 +300,6 @@ namespace TweetDck.Core{
StopMouseHook();
}
public void OnNotificationReady(){
UpdateTitle();
PrepareAndDisplayWindow();
timerProgress.Start();
}
public void FinishCurrentTweet(){
if (tweetQueue.Count > 0){
LoadNextNotification();
@@ -350,10 +350,6 @@ namespace TweetDck.Core{
string bodyClasses = browser.Bounds.Contains(PointToClient(Cursor.Position)) ? "td-hover" : string.Empty;
browser.LoadHtml(tweet.GenerateHtml(bodyClasses), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
if (Program.UserConfig.NotificationLegacyLoad){
OnNotificationReady();
}
}
private void PrepareAndDisplayWindow(){
@@ -427,6 +423,12 @@ namespace TweetDck.Core{
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
}
protected void OnNotificationReady(){
UpdateTitle();
PrepareAndDisplayWindow();
timerProgress.Start();
}
public void DisplayTooltip(string text){
if (string.IsNullOrEmpty(text)){
toolTip.Hide(this);
@@ -434,7 +436,7 @@ namespace TweetDck.Core{
else{
Point position = PointToClient(Cursor.Position);
position.Offset(20, 5);
toolTip.Show(text, this, position);
toolTip.Show(text, this, position); // TODO figure out flickering when moving the mouse
}
}
}

View File

@@ -3,6 +3,7 @@ using System;
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Bridge;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Handling{
@@ -21,6 +22,12 @@ namespace TweetDck.Core.Handling{
}
#endif
private readonly Form form;
protected ContextMenuBase(Form form){
this.form = form;
}
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){
model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser");
@@ -43,7 +50,7 @@ namespace TweetDck.Core.Handling{
break;
case MenuCopyLinkUrl:
Clipboard.SetText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink, TextDataFormat.UnicodeText);
SetClipboardText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink);
break;
case MenuOpenImage:
@@ -74,7 +81,7 @@ namespace TweetDck.Core.Handling{
break;
case MenuCopyImageUrl:
Clipboard.SetText(parameters.SourceUrl, TextDataFormat.UnicodeText);
SetClipboardText(parameters.SourceUrl);
break;
#if DEBUG
@@ -93,6 +100,10 @@ namespace TweetDck.Core.Handling{
return false;
}
protected void SetClipboardText(string text){
form.InvokeSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText));
}
protected static void RemoveSeparatorIfLast(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);

View File

@@ -1,5 +1,4 @@
using CefSharp;
using System.Windows.Forms;
using TweetDck.Core.Bridge;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
@@ -23,7 +22,7 @@ namespace TweetDck.Core.Handling{
private string lastHighlightedTweet;
private string lastHighlightedQuotedTweet;
public ContextMenuBrowser(FormBrowser form){
public ContextMenuBrowser(FormBrowser form) : base(form){
this.form = form;
}
@@ -34,6 +33,10 @@ namespace TweetDck.Core.Handling{
model.Remove(CefMenuCommand.ViewSource);
RemoveSeparatorIfLast(model);
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
model.AddSeparator();
}
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
@@ -77,6 +80,8 @@ namespace TweetDck.Core.Handling{
AddDebugMenuItems(globalMenu);
#endif
}
RemoveSeparatorIfLast(model);
}
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
@@ -114,7 +119,7 @@ namespace TweetDck.Core.Handling{
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(lastHighlightedTweet, TextDataFormat.UnicodeText);
SetClipboardText(lastHighlightedTweet);
return true;
case MenuScreenshotTweet:
@@ -126,7 +131,7 @@ namespace TweetDck.Core.Handling{
return true;
case MenuCopyQuotedTweetUrl:
Clipboard.SetText(lastHighlightedQuotedTweet, TextDataFormat.UnicodeText);
SetClipboardText(lastHighlightedQuotedTweet);
return true;
}

View File

@@ -1,5 +1,4 @@
using System.Windows.Forms;
using CefSharp;
using CefSharp;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Handling{
@@ -12,13 +11,19 @@ namespace TweetDck.Core.Handling{
private readonly FormNotification form;
private readonly bool enableCustomMenu;
public ContextMenuNotification(FormNotification form, bool enableCustomMenu){
public ContextMenuNotification(FormNotification form, bool enableCustomMenu) : base(form){
this.form = form;
this.enableCustomMenu = enableCustomMenu;
}
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear();
if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection)){
model.AddItem(CefMenuCommand.Copy, "Copy");
model.AddSeparator();
}
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
if (enableCustomMenu){
@@ -62,11 +67,11 @@ namespace TweetDck.Core.Handling{
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(form.CurrentUrl, TextDataFormat.UnicodeText);
SetClipboardText(form.CurrentUrl);
return true;
case MenuCopyQuotedTweetUrl:
Clipboard.SetText(form.CurrentQuotedTweetUrl, TextDataFormat.UnicodeText);
SetClipboardText(form.CurrentQuotedTweetUrl);
return true;
}

View File

@@ -7,6 +7,7 @@ namespace TweetDck.Core.Notification{
AutoHide = 1,
DisableScripts = 2,
DisableContextMenu = 4,
TopMost = 8
TopMost = 8,
ManualDisplay = 16
}
}

View File

@@ -7,24 +7,24 @@ using TweetDck.Resources;
namespace TweetDck.Core.Notification.Screenshot{
sealed class FormNotificationScreenshotable : FormNotification{
public FormNotificationScreenshotable(FormBrowser owner, NotificationFlags flags) : base(owner, null, flags){
UpdateTitle();
}
public void PrepareNotificationForScreenshot(Action callback){
public FormNotificationScreenshotable(Action callback, FormBrowser owner, NotificationFlags flags) : base(owner, null, flags){
browser.RegisterAsyncJsObject("$TD_NotificationScreenshot", new CallbackBridge(this, callback));
browser.FrameLoadEnd += (sender, args) => {
if (args.Frame.IsMain && browser.Address != "about:blank"){
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout(() => $TD_NotificationScreenshot.trigger(), 25)", "gen:screenshot");
ScriptLoader.ExecuteScript(args.Frame, "window.setTimeout($TD_NotificationScreenshot.trigger, 25)", "gen:screenshot");
}
};
UpdateTitle();
}
public void LoadNotificationForScreenshot(TweetNotification tweet, int width, int height){
browser.LoadHtml(tweet.GenerateHtml(enableCustomCSS: false), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
Location = ControlExtensions.InvisibleLocation;
FormBorderStyle = Program.UserConfig.ShowScreenshotBorder ? FormBorderStyle.FixedToolWindow : FormBorderStyle.None;
SetNotificationSize(width, height, false);
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Notification.Screenshot{
@@ -13,12 +11,10 @@ namespace TweetDck.Core.Notification.Screenshot{
public TweetScreenshotManager(FormBrowser browser){
this.browser = browser;
this.screenshot = new FormNotificationScreenshotable(browser, NotificationFlags.DisableScripts | NotificationFlags.DisableContextMenu | NotificationFlags.TopMost){
this.screenshot = new FormNotificationScreenshotable(Callback, browser, NotificationFlags.DisableScripts | NotificationFlags.DisableContextMenu | NotificationFlags.TopMost | NotificationFlags.ManualDisplay){
CanMoveWindow = () => false
};
this.screenshot.PrepareNotificationForScreenshot(Callback);
this.timeout = WindowsUtils.CreateSingleTickTimer(10000);
this.timeout.Tick += (sender, args) => screenshot.Reset();
}

View File

@@ -59,10 +59,6 @@ namespace TweetDck.Core.Notification{
TopLeft, TopRight, BottomLeft, BottomRight, Custom
}
public enum Duration{
Short, Medium, Long, VeryLong
}
public string Url{
get{
return url;

View File

@@ -9,9 +9,13 @@ using TweetDck.Updates;
namespace TweetDck.Core.Other{
sealed partial class FormSettings : Form{
public const int TabIndexNotification = 1;
private readonly FormBrowser browser;
private readonly Dictionary<Type, BaseTabSettings> tabs = new Dictionary<Type, BaseTabSettings>(4);
private bool wasTabSelectedAutomatically;
public FormSettings(FormBrowser browser, PluginManager plugins, UpdateHandler updates){
InitializeComponent();
@@ -22,12 +26,18 @@ namespace TweetDck.Core.Other{
this.tabPanel.SetupTabPanel(100);
this.tabPanel.AddButton("General", SelectTab<TabSettingsGeneral>);
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browser.CreateNotificationForm(NotificationFlags.DisableContextMenu))));
this.tabPanel.AddButton("Notifications", () => SelectTab(() => new TabSettingsNotifications(browser.CreateNotificationForm(NotificationFlags.DisableContextMenu), wasTabSelectedAutomatically)));
this.tabPanel.AddButton("Updates", () => SelectTab(() => new TabSettingsUpdates(updates)));
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browser.ReloadBrowser, plugins)));
this.tabPanel.AddButton("Advanced", () => SelectTab(() => new TabSettingsAdvanced(browser.ReinjectCustomCSS, plugins)));
this.tabPanel.SelectTab(tabPanel.Buttons.First());
}
public void SelectTab(int index){
wasTabSelectedAutomatically = true;
this.tabPanel.SelectTab(tabPanel.Buttons.ElementAt(index));
wasTabSelectedAutomatically = false;
}
private void SelectTab<T>() where T : BaseTabSettings, new(){
SelectTab(() => new T());
}

View File

@@ -23,6 +23,7 @@
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.textBoxBrowserCSS = new System.Windows.Forms.TextBox();
this.btnCancel = new System.Windows.Forms.Button();
this.btnApply = new System.Windows.Forms.Button();
@@ -32,6 +33,7 @@
this.textBoxNotificationCSS = new System.Windows.Forms.TextBox();
this.labelWarning = new System.Windows.Forms.Label();
this.btnOpenWiki = new System.Windows.Forms.Button();
this.timerTestBrowser = new System.Windows.Forms.Timer(this.components);
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
this.splitContainer.Panel1.SuspendLayout();
this.splitContainer.Panel2.SuspendLayout();
@@ -52,6 +54,7 @@
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
this.textBoxBrowserCSS.TabIndex = 0;
this.textBoxBrowserCSS.WordWrap = false;
this.textBoxBrowserCSS.KeyUp += new System.Windows.Forms.KeyEventHandler(this.textBoxBrowserCSS_KeyUp);
//
// btnCancel
//
@@ -132,7 +135,7 @@
this.textBoxNotificationCSS.Multiline = true;
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxNotificationCSS.Size = new System.Drawing.Size(376, 253);
this.textBoxNotificationCSS.Size = new System.Drawing.Size(373, 253);
this.textBoxNotificationCSS.TabIndex = 1;
this.textBoxNotificationCSS.WordWrap = false;
//
@@ -159,6 +162,11 @@
this.btnOpenWiki.UseVisualStyleBackColor = true;
this.btnOpenWiki.Click += new System.EventHandler(this.btnOpenWiki_Click);
//
// timerTestBrowser
//
this.timerTestBrowser.Interval = 500;
this.timerTestBrowser.Tick += new System.EventHandler(this.timerTestBrowser_Tick);
//
// DialogSettingsCSS
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -195,5 +203,6 @@
private System.Windows.Forms.Label labelNotification;
private System.Windows.Forms.Label labelWarning;
private System.Windows.Forms.Button btnOpenWiki;
private System.Windows.Forms.Timer timerTestBrowser;
}
}

View File

@@ -17,10 +17,14 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
}
}
public DialogSettingsCSS(){
private readonly Action<string> reinjectBrowserCSS;
public DialogSettingsCSS(Action<string> reinjectBrowserCSS){
InitializeComponent();
Text = Program.BrandName+" Settings - CSS";
this.reinjectBrowserCSS = reinjectBrowserCSS;
textBoxBrowserCSS.EnableMultilineShortcuts();
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
@@ -29,6 +33,16 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
}
private void textBoxBrowserCSS_KeyUp(object sender, KeyEventArgs e){
timerTestBrowser.Stop();
timerTestBrowser.Start();
}
private void timerTestBrowser_Tick(object sender, EventArgs e){
reinjectBrowserCSS(textBoxBrowserCSS.Text);
timerTestBrowser.Stop();
}
private void btnOpenWiki_Click(object sender, EventArgs e){
BrowserUtils.OpenExternalBrowser("https://github.com/chylex/TweetDuck/wiki");
}

View File

@@ -121,8 +121,11 @@ namespace TweetDck.Core.Other.Settings.Export{
public void WriteToFile(string path, bool createDirectory){
if (createDirectory){
// ReSharper disable once AssignNullToNotNullAttribute
Directory.CreateDirectory(Path.GetDirectoryName(path));
string dir = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(dir)){
Directory.CreateDirectory(dir);
}
}
File.WriteAllBytes(path, contents);

View File

@@ -9,13 +9,13 @@ using TweetDck.Plugins;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsAdvanced : BaseTabSettings{
private readonly Action browserReloadAction;
private readonly Action<string> reinjectBrowserCSS;
private readonly PluginManager plugins;
public TabSettingsAdvanced(Action browserReloadAction, PluginManager plugins){
public TabSettingsAdvanced(Action<string> reinjectBrowserCSS, PluginManager plugins){
InitializeComponent();
this.browserReloadAction = browserReloadAction;
this.reinjectBrowserCSS = reinjectBrowserCSS;
this.plugins = plugins;
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
@@ -80,17 +80,13 @@ namespace TweetDck.Core.Other.Settings{
}
private void btnEditCSS_Click(object sender, EventArgs e){
using(DialogSettingsCSS form = new DialogSettingsCSS()){
using(DialogSettingsCSS form = new DialogSettingsCSS(reinjectBrowserCSS)){
if (form.ShowDialog(ParentForm) == DialogResult.OK){
bool hasChangedBrowser = form.BrowserCSS != Config.CustomBrowserCSS;
Config.CustomBrowserCSS = form.BrowserCSS;
Config.CustomNotificationCSS = form.NotificationCSS;
if (hasChangedBrowser && MessageBox.Show("The browser CSS has changed, do you want to reload it?", "Browser CSS Changed", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes){
browserReloadAction();
}
}
reinjectBrowserCSS(Config.CustomBrowserCSS); // reinject on cancel too, because the CSS is updated while typing
}
}

View File

@@ -29,6 +29,7 @@
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.checkTrayHighlight = new System.Windows.Forms.CheckBox();
this.checkSpellCheck = new System.Windows.Forms.CheckBox();
this.checkScreenshotBorder = new System.Windows.Forms.CheckBox();
this.groupTray = new System.Windows.Forms.GroupBox();
this.labelTrayIcon = new System.Windows.Forms.Label();
this.groupInterface = new System.Windows.Forms.GroupBox();
@@ -87,12 +88,24 @@
this.checkSpellCheck.UseVisualStyleBackColor = true;
this.checkSpellCheck.CheckedChanged += new System.EventHandler(this.checkSpellCheck_CheckedChanged);
//
// checkScreenshotBorder
//
this.checkScreenshotBorder.AutoSize = true;
this.checkScreenshotBorder.Location = new System.Drawing.Point(9, 67);
this.checkScreenshotBorder.Name = "checkScreenshotBorder";
this.checkScreenshotBorder.Size = new System.Drawing.Size(169, 17);
this.checkScreenshotBorder.TabIndex = 16;
this.checkScreenshotBorder.Text = "Include Border In Screenshots";
this.toolTip.SetToolTip(this.checkScreenshotBorder, "Shows the window border in tweet screenshots.");
this.checkScreenshotBorder.UseVisualStyleBackColor = true;
this.checkScreenshotBorder.CheckedChanged += new System.EventHandler(this.checkScreenshotBorder_CheckedChanged);
//
// groupTray
//
this.groupTray.Controls.Add(this.checkTrayHighlight);
this.groupTray.Controls.Add(this.labelTrayIcon);
this.groupTray.Controls.Add(this.comboBoxTrayType);
this.groupTray.Location = new System.Drawing.Point(9, 86);
this.groupTray.Location = new System.Drawing.Point(9, 109);
this.groupTray.Name = "groupTray";
this.groupTray.Size = new System.Drawing.Size(183, 93);
this.groupTray.TabIndex = 15;
@@ -111,11 +124,12 @@
//
// groupInterface
//
this.groupInterface.Controls.Add(this.checkScreenshotBorder);
this.groupInterface.Controls.Add(this.checkSpellCheck);
this.groupInterface.Controls.Add(this.checkExpandLinks);
this.groupInterface.Location = new System.Drawing.Point(9, 9);
this.groupInterface.Name = "groupInterface";
this.groupInterface.Size = new System.Drawing.Size(183, 71);
this.groupInterface.Size = new System.Drawing.Size(183, 90);
this.groupInterface.TabIndex = 16;
this.groupInterface.TabStop = false;
this.groupInterface.Text = "User Interface";
@@ -146,5 +160,6 @@
private System.Windows.Forms.Label labelTrayIcon;
private System.Windows.Forms.CheckBox checkTrayHighlight;
private System.Windows.Forms.CheckBox checkSpellCheck;
private System.Windows.Forms.CheckBox checkScreenshotBorder;
}
}

View File

@@ -14,6 +14,7 @@ namespace TweetDck.Core.Other.Settings{
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkSpellCheck.Checked = Config.EnableSpellCheck;
checkScreenshotBorder.Checked = Config.ShowScreenshotBorder;
checkTrayHighlight.Checked = Config.EnableTrayHighlight;
}
@@ -30,6 +31,12 @@ namespace TweetDck.Core.Other.Settings{
PromptRestart();
}
private void checkScreenshotBorder_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.ShowScreenshotBorder = checkScreenshotBorder.Checked;
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
if (!Ready)return;

View File

@@ -44,13 +44,12 @@
this.trackBarDuration = new System.Windows.Forms.TrackBar();
this.groupUserInterface = new System.Windows.Forms.GroupBox();
this.checkTimerCountDown = new System.Windows.Forms.CheckBox();
this.checkLegacyLoad = new System.Windows.Forms.CheckBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.groupCustomSound = new System.Windows.Forms.GroupBox();
this.tbCustomSound = new System.Windows.Forms.TextBox();
this.btnBrowseSound = new System.Windows.Forms.Button();
this.btnResetSound = new System.Windows.Forms.Button();
this.btnBrowseSound = new System.Windows.Forms.Button();
this.tbCustomSound = new System.Windows.Forms.TextBox();
this.groupNotificationLocation.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.groupNotificationDuration.SuspendLayout();
@@ -204,7 +203,7 @@
this.groupNotificationDuration.Controls.Add(this.tableLayoutDurationButtons);
this.groupNotificationDuration.Controls.Add(this.labelDurationValue);
this.groupNotificationDuration.Controls.Add(this.trackBarDuration);
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 106);
this.groupNotificationDuration.Location = new System.Drawing.Point(9, 83);
this.groupNotificationDuration.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 89);
this.groupNotificationDuration.TabIndex = 9;
@@ -307,11 +306,10 @@
// groupUserInterface
//
this.groupUserInterface.Controls.Add(this.checkTimerCountDown);
this.groupUserInterface.Controls.Add(this.checkLegacyLoad);
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Location = new System.Drawing.Point(9, 9);
this.groupUserInterface.Name = "groupUserInterface";
this.groupUserInterface.Size = new System.Drawing.Size(183, 91);
this.groupUserInterface.Size = new System.Drawing.Size(183, 68);
this.groupUserInterface.TabIndex = 10;
this.groupUserInterface.TabStop = false;
this.groupUserInterface.Text = "General";
@@ -328,19 +326,6 @@
this.checkTimerCountDown.UseVisualStyleBackColor = true;
this.checkTimerCountDown.CheckedChanged += new System.EventHandler(this.checkTimerCountDown_CheckedChanged);
//
// checkLegacyLoad
//
this.checkLegacyLoad.AutoSize = true;
this.checkLegacyLoad.Location = new System.Drawing.Point(6, 67);
this.checkLegacyLoad.Name = "checkLegacyLoad";
this.checkLegacyLoad.Size = new System.Drawing.Size(139, 17);
this.checkLegacyLoad.TabIndex = 5;
this.checkLegacyLoad.Text = "Legacy Loading System";
this.toolTip.SetToolTip(this.checkLegacyLoad, "Try enabling if notifications do not display.\r\nMight cause delays and visual arti" +
"facts.");
this.checkLegacyLoad.UseVisualStyleBackColor = true;
this.checkLegacyLoad.CheckedChanged += new System.EventHandler(this.checkLegacyLoad_CheckedChanged);
//
// checkNotificationTimer
//
this.checkNotificationTimer.AutoSize = true;
@@ -359,19 +344,24 @@
this.groupCustomSound.Controls.Add(this.btnResetSound);
this.groupCustomSound.Controls.Add(this.btnBrowseSound);
this.groupCustomSound.Controls.Add(this.tbCustomSound);
this.groupCustomSound.Location = new System.Drawing.Point(9, 201);
this.groupCustomSound.Location = new System.Drawing.Point(9, 178);
this.groupCustomSound.Name = "groupCustomSound";
this.groupCustomSound.Size = new System.Drawing.Size(183, 72);
this.groupCustomSound.TabIndex = 11;
this.groupCustomSound.TabStop = false;
this.groupCustomSound.Text = "Custom Sound";
//
// tbCustomSound
// btnResetSound
//
this.tbCustomSound.Location = new System.Drawing.Point(6, 19);
this.tbCustomSound.Name = "tbCustomSound";
this.tbCustomSound.Size = new System.Drawing.Size(170, 20);
this.tbCustomSound.TabIndex = 0;
this.btnResetSound.AutoSize = true;
this.btnResetSound.Location = new System.Drawing.Point(126, 43);
this.btnResetSound.Name = "btnResetSound";
this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnResetSound.Size = new System.Drawing.Size(51, 23);
this.btnResetSound.TabIndex = 2;
this.btnResetSound.Text = "Reset";
this.btnResetSound.UseVisualStyleBackColor = true;
this.btnResetSound.Click += new System.EventHandler(this.btnResetSound_Click);
//
// btnBrowseSound
//
@@ -385,17 +375,13 @@
this.btnBrowseSound.UseVisualStyleBackColor = true;
this.btnBrowseSound.Click += new System.EventHandler(this.btnBrowseSound_Click);
//
// btnResetSound
// tbCustomSound
//
this.btnResetSound.AutoSize = true;
this.btnResetSound.Location = new System.Drawing.Point(126, 43);
this.btnResetSound.Name = "btnResetSound";
this.btnResetSound.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnResetSound.Size = new System.Drawing.Size(51, 23);
this.btnResetSound.TabIndex = 2;
this.btnResetSound.Text = "Reset";
this.btnResetSound.UseVisualStyleBackColor = true;
this.btnResetSound.Click += new System.EventHandler(this.btnResetSound_Click);
this.tbCustomSound.Location = new System.Drawing.Point(6, 19);
this.tbCustomSound.Name = "tbCustomSound";
this.tbCustomSound.Size = new System.Drawing.Size(170, 20);
this.tbCustomSound.TabIndex = 0;
this.tbCustomSound.TextChanged += new System.EventHandler(this.tbCustomSound_TextChanged);
//
// TabSettingsNotifications
//
@@ -440,7 +426,6 @@
private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.ToolTip toolTip;
private System.Windows.Forms.Label labelEdgeDistanceValue;
private System.Windows.Forms.CheckBox checkLegacyLoad;
private System.Windows.Forms.CheckBox checkTimerCountDown;
private System.Windows.Forms.Label labelDurationValue;
private System.Windows.Forms.TrackBar trackBarDuration;

View File

@@ -1,6 +1,7 @@
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Core.Notification;
@@ -11,7 +12,7 @@ namespace TweetDck.Core.Other.Settings{
private readonly FormNotification notification;
private readonly Point initCursorPosition;
public TabSettingsNotifications(FormNotification notification){
public TabSettingsNotifications(FormNotification notification, bool ignoreAutoClick){
InitializeComponent();
this.notification = notification;
@@ -30,7 +31,7 @@ namespace TweetDck.Core.Other.Settings{
this.notification.Activated += notification_Activated;
this.notification.Show(this);
initCursorPosition = Cursor.Position;
initCursorPosition = ignoreAutoClick ? ControlExtensions.InvisibleLocation : Cursor.Position;
switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
@@ -54,12 +55,11 @@ namespace TweetDck.Core.Other.Settings{
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkTimerCountDown.Enabled = checkNotificationTimer.Checked;
checkTimerCountDown.Checked = Config.NotificationTimerCountDown;
checkLegacyLoad.Checked = Config.NotificationLegacyLoad;
trackBarEdgeDistance.SetValueSafe(Config.NotificationEdgeDistance);
labelEdgeDistanceValue.Text = trackBarEdgeDistance.Value.ToString(CultureInfo.InvariantCulture)+" px";
tbCustomSound.Text = Config.NotificationSoundPath ?? string.Empty;
tbCustomSound.Text = Config.NotificationSoundPath;
Disposed += (sender, args) => this.notification.Dispose();
}
@@ -78,7 +78,7 @@ namespace TweetDck.Core.Other.Settings{
}
private void notification_Activated(object sender, EventArgs e){
if (Cursor.Position == initCursorPosition){
if (Cursor.Position == initCursorPosition && initCursorPosition != ControlExtensions.InvisibleLocation){
Timer delay = WindowsUtils.CreateSingleTickTimer(1);
delay.Tick += (sender2, args2) => { // here you can see a disgusting hack to force the freshly opened notification window out of focus
@@ -153,12 +153,6 @@ namespace TweetDck.Core.Other.Settings{
notification.ShowNotificationForSettings(true);
}
private void checkLegacyLoad_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.NotificationLegacyLoad = checkLegacyLoad.Checked;
}
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
if (!Ready)return;
@@ -174,6 +168,13 @@ namespace TweetDck.Core.Other.Settings{
notification.ShowNotificationForSettings(false);
}
private void tbCustomSound_TextChanged(object sender, EventArgs e){
// also runs when the control is created, i.e. when Ready is false
bool fileExists = string.IsNullOrEmpty(tbCustomSound.Text) || File.Exists(tbCustomSound.Text);
tbCustomSound.ForeColor = fileExists ? SystemColors.WindowText : Color.Maroon;
}
private void btnBrowseSound_Click(object sender, EventArgs e){
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,

View File

@@ -1,6 +1,7 @@
using System;
using System.Windows.Forms;
using TweetDck.Updates;
using TweetDck.Updates.Events;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsUpdates : BaseTabSettings{
@@ -27,12 +28,18 @@ namespace TweetDck.Core.Other.Settings{
private void btnCheckUpdates_Click(object sender, EventArgs e){
if (!Ready)return;
Config.DismissedUpdate = string.Empty;
Config.Save();
updateCheckEventId = updates.Check(true);
btnCheckUpdates.Enabled = false;
if (updateCheckEventId == -1){
MessageBox.Show("Sorry, your system is no longer supported.", "Unsupported System", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else{
btnCheckUpdates.Enabled = false;
updates.Settings.DismissedUpdate = string.Empty;
Config.DismissedUpdate = string.Empty;
Config.Save();
}
}
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){

View File

@@ -105,7 +105,7 @@ namespace TweetDck.Core.Utils{
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
}
return build.Remove(build.Length-1, 1).ToString();
return build.Length == 0 ? string.Empty : build.Remove(build.Length-1, 1).ToString();
}
}
}

View File

@@ -24,13 +24,26 @@ namespace TweetDck.Core.Utils{
Left, Right
}
private struct LASTINPUTINFO{
public static readonly uint Size = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
// ReSharper disable once NotAccessedField.Local
public uint cbSize;
#pragma warning disable 649
public uint dwTime;
#pragma warning restore 649
}
public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public 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 void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
private static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO info);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
@@ -75,5 +88,23 @@ namespace TweetDck.Core.Utils{
mouse_event(flagHold, Cursor.Position.X, Cursor.Position.Y, 0, 0);
mouse_event(flagRelease, Cursor.Position.X, Cursor.Position.Y, 0, 0);
}
public static int GetIdleSeconds(){
LASTINPUTINFO info = new LASTINPUTINFO();
info.cbSize = LASTINPUTINFO.Size;
if (!GetLastInputInfo(ref info)){
return 0;
}
uint ticks;
unchecked{
ticks = (uint)Environment.TickCount;
}
int seconds = (int)Math.Floor(TimeSpan.FromMilliseconds(ticks-info.dwTime).TotalSeconds);
return Math.Max(0, seconds); // ignore rollover after several weeks of uptime
}
}
}

View File

@@ -1,9 +1,14 @@
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace TweetDck.Core.Utils{
static class WindowsUtils{
private static readonly Regex RegexStripHtmlStyles = new Regex(@"\s?(?:style|class)="".*?""");
private static readonly Regex RegexOffsetClipboardHtml = new Regex(@"(?<=EndHTML:|EndFragment:)(\d+)");
public static bool CheckFolderWritePermission(string path){
string testFile = Path.Combine(path, ".test");
@@ -39,5 +44,42 @@ namespace TweetDck.Core.Utils{
timer.Tick += (sender, args) => timer.Stop();
return timer;
}
public static void ClipboardStripHtmlStyles(){
if (!Clipboard.ContainsText(TextDataFormat.Html)){
return;
}
string originalText = Clipboard.GetText(TextDataFormat.UnicodeText);
string originalHtml = Clipboard.GetText(TextDataFormat.Html);
string updatedHtml = RegexStripHtmlStyles.Replace(originalHtml, string.Empty);
int removed = originalHtml.Length-updatedHtml.Length;
updatedHtml = RegexOffsetClipboardHtml.Replace(updatedHtml, match => (int.Parse(match.Value)-removed).ToString().PadLeft(match.Value.Length, '0'));
DataObject obj = new DataObject();
obj.SetText(originalText, TextDataFormat.UnicodeText);
obj.SetText(updatedHtml, TextDataFormat.Html);
SetClipboardData(obj);
}
public static void SetClipboard(string text, TextDataFormat format){
if (string.IsNullOrEmpty(text)){
return;
}
DataObject obj = new DataObject();
obj.SetText(text, format);
SetClipboardData(obj);
}
private static void SetClipboardData(DataObject obj){
try{
Clipboard.SetDataObject(obj);
}catch(ExternalException e){
Program.Reporter.HandleException("Clipboard Error", Program.BrandName+" could not access the clipboard as it is currently used by another process.", true, e);
}
}
}
}

View File

@@ -21,8 +21,8 @@ namespace TweetDck{
public const string BrandName = "TweetDuck";
public const string Website = "https://tweetduck.chylex.com";
public const string VersionTag = "1.5.1";
public const string VersionFull = "1.5.1.0";
public const string VersionTag = "1.6.3";
public const string VersionFull = "1.6.3.0";
public static readonly Version Version = new Version(VersionTag);
@@ -125,6 +125,8 @@ namespace TweetDck{
}
BrowserCache.ClearOldCacheFiles();
CefSharpSettings.WcfEnabled = false;
CefSettings settings = new CefSettings{
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
@@ -155,7 +157,8 @@ namespace TweetDck{
plugins.Reload();
FormBrowser mainForm = new FormBrowser(plugins, new UpdaterSettings{
AllowPreReleases = Args.HasFlag("-debugupdates")
AllowPreReleases = Args.HasFlag("-debugupdates"),
DismissedUpdate = UserConfig.DismissedUpdate
});
Application.Run(mainForm);

View File

@@ -36,4 +36,8 @@ using TweetDck;
[assembly: AssemblyVersion(Program.VersionFull)]
[assembly: AssemblyFileVersion(Program.VersionFull)]
[assembly: NeutralResourcesLanguageAttribute("en")]
[assembly: NeutralResourcesLanguageAttribute("en")]
#if DEBUG
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTests")]
#endif

View File

@@ -1,4 +1,4 @@
(function($, $TD, TD){
(function($, $TD, $TDX, TD){
//
// Variable: Current highlighted column jQuery object.
//
@@ -20,8 +20,8 @@
if (menu.length === 0)return;
menu.children(".drp-h-divider").last().after([
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">'+$TD.brandName+' settings</a></li>',
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">'+$TD.brandName+' plugins</a></li>',
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">TweetDuck settings</a></li>',
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">TweetDuck plugins</a></li>',
'<li class="drp-h-divider"></li>'
].join(""));
@@ -112,6 +112,7 @@
html.css("border", "0");
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
html.find(".js-quote-detail").removeClass("is-actionable");
var url = html.find("time").first().children("a").first().attr("href") || "";
@@ -198,7 +199,7 @@
return;
}
if ($TD.expandLinksOnHover){
if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = me.attr("data-full-url");
expanded = cutStart(expanded, "https://");
@@ -217,7 +218,7 @@
}
}
else if (e.type === "mouseleave"){
if ($TD.expandLinksOnHover){
if ($TDX.expandLinksOnHover){
var prevText = me.attr("td-prev-text");
if (prevText){
@@ -256,7 +257,7 @@
var soundEle = document.getElementById("update-sound");
soundEle.play = prependToFunction(soundEle.play, function(){
return $TD.muteNotifications || $TD.hasCustomNotificationSound;
return $TDX.muteNotifications || $TDX.hasCustomNotificationSound;
});
})();
@@ -524,6 +525,13 @@
});
})();
//
// Block: Work around clipboard HTML formatting.
//
$(document).on("copy", function(e){
window.setTimeout($TD.fixClipboard, 0);
});
//
// Block: Inject custom CSS and layout into the page.
//
@@ -536,12 +544,14 @@
styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment
if ($TD.hasCustomBrowserCSS){
var styleCustom = document.createElement("style");
styleCustom.innerHTML = $TD.customBrowserCSS;
document.head.appendChild(styleCustom);
}
TD.services.TwitterActionRetweetedRetweet.prototype.iconClass = "icon-retweet icon-retweet-color txt-base-medium"; // fix retweet icon mismatch
window.TDGF_reinjectCustomCSS = function(styles){
$("#tweetduck-custom-css").remove();
if (styles && styles.length){
$(document.head).append("<style type='text/css' id='tweetduck-custom-css'>"+styles+"</style>");
}
};
})();
})($, $TD, TD);
})($, $TD, $TDX, TD);

View File

@@ -1,19 +1,49 @@
(function($, $TD, TD){
(function($, $TD, $TDX, TD){
var isDebugging = false;
$(document).keydown(function(e){
// ==============================
// F4 key - simulate notification
// ==============================
// ==========================
// F4 key - toggle debug mode
// ==========================
if (e.keyCode === 115){
var col = TD.controller.columnManager.getAllOrdered()[0];
isDebugging = !isDebugging;
$(".app-title").first().css("background-color", isDebugging ? "#5A6B75" : "#292F33");
}
// Debug mode handling
else if (isDebugging){
e.preventDefault();
$.publish("/notifications/new",[{
column: col,
items: [
col.updateArray[Math.floor(Math.random()*col.updateArray.length)]
]
}]);
// ===================================
// N key - simulate popup notification
// ===================================
if (e.keyCode === 78){
var col = TD.controller.columnManager.getAllOrdered()[0];
$.publish("/notifications/new",[{
column: col,
items: [
col.updateArray[Math.floor(Math.random()*col.updateArray.length)]
]
}]);
}
// ===================================
// S key - simulate sound notification
// ===================================
else if (e.keyCode === 83){
if ($TDX.hasCustomNotificationSound){
$TD.onTweetSound();
}
else{
document.getElementById("update-sound").play();
}
}
}
});
})($, $TD, TD);
})($, $TD, $TDX, TD);

View File

@@ -1,4 +1,4 @@
(function($TD){
(function($TD, $TDX){
//
// Variable: Collection of all <a> tags.
//
@@ -51,7 +51,7 @@
return;
}
if ($TD.expandLinksOnHover){
if ($TDX.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = url;
expanded = cutStart(expanded, "https://");
@@ -73,7 +73,7 @@
addEventListener(links, "mouseleave", function(e){
if (!e.currentTarget.hasAttribute("data-full-url"))return;
if ($TD.expandLinksOnHover){
if ($TDX.expandLinksOnHover){
var prevText = e.currentTarget.getAttribute("td-prev-text");
if (prevText){
@@ -146,9 +146,4 @@
document.body.addEventListener("mouseleave", function(){
document.body.classList.remove("td-hover");
});
//
// Block: Page fully loaded.
//
$TD.onNotificationReady();
})($TD);
})($TD, $TDX);

View File

@@ -7,25 +7,25 @@
//
// Constant: Update exe file name.
//
const updateFileName = $TDU.brandName+".Update.exe";
const updateFileName = "TweetDuck.Update.exe";
//
// Constant: Url that returns JSON data about latest version.
//
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases/latest";
const updateCheckUrlLatest = "https://api.github.com/repos/chylex/TweetDuck/releases/latest";
//
// Constant: Url that returns JSON data about all versions, including prereleases.
//
const updateCheckUrlAll = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases";
const updateCheckUrlAll = "https://api.github.com/repos/chylex/TweetDuck/releases";
//
// Function: Creates the update notification element. Removes the old one if already exists.
//
var createUpdateNotificationElement = function(version, download){
var displayNotification = function(version, download){
var outdated = version === "unsupported";
var ele = $("#tweetdck-update");
var ele = $("#tweetduck-update");
var existed = ele.length > 0;
if (existed > 0){
@@ -33,7 +33,7 @@
}
var html = outdated ? [
"<div id='tweetdck-update'>",
"<div id='tweetduck-update'>",
"<p class='tdu-title'>Unsupported System</p>",
"<p class='tdu-info'>You will not receive updates.</p>",
"<div class='tdu-buttons'>",
@@ -42,8 +42,8 @@
"</div>",
"</div>"
] : [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>"+$TDU.brandName+" Update</p>",
"<div id='tweetduck-update'>",
"<p class='tdu-title'>TweetDuck Update</p>",
"<p class='tdu-info'>Version "+version+" is now available.</p>",
"<div class='tdu-buttons'>",
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</span></button>",
@@ -54,7 +54,7 @@
$(document.body).append(html.join(""));
ele = $("#tweetdck-update");
ele = $("#tweetduck-update");
var buttonDiv = ele.children("div.tdu-buttons").first();
@@ -128,33 +128,19 @@
//
// Function: Runs an update check and updates all DOM elements appropriately.
//
var runUpdateCheck = function(force, eventID){
if (!$TDU.isSystemSupported){
if ($TDU.dismissedVersionTag !== "unsupported"){
createUpdateNotificationElement("unsupported");
}
return;
}
var runUpdateCheck = function(eventID, versionTag, dismissedVersionTag, allowPre){
clearTimeout(updateCheckTimeoutID);
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
if (!$TDU.updateCheckEnabled && !force){
return;
}
var allowPre = $TDU.allowPreReleases;
updateCheckTimeoutID = setTimeout($TDU.triggerUpdateCheck, 1000*60*60); // 1 hour
$.getJSON(allowPre ? updateCheckUrlAll : updateCheckUrlLatest, function(response){
var release = allowPre ? response[0] : response;
var tagName = release.tag_name;
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && release.assets.length > 0;
var hasUpdate = tagName !== versionTag && tagName !== dismissedVersionTag && release.assets.length > 0;
if (hasUpdate){
var obj = release.assets.find(asset => asset.name === updateFileName) || release.assets[0];
createUpdateNotificationElement(tagName, obj.browser_download_url);
displayNotification(tagName, obj.browser_download_url);
}
if (eventID){ // ignore undefined and 0
@@ -166,6 +152,6 @@
//
// Block: Setup global functions.
//
window.TDUF_displayNotification = displayNotification;
window.TDUF_runUpdateCheck = runUpdateCheck;
runUpdateCheck();
})($, $TDU);

View File

@@ -71,6 +71,7 @@
<ItemGroup>
<Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\UserConfig.cs" />
<Compile Include="Core\Bridge\PropertyBridge.cs" />
<Compile Include="Core\Controls\ControlExtensions.cs" />
<Compile Include="Core\Controls\FlatButton.cs">
<SubType>Component</SubType>
@@ -222,6 +223,7 @@
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Plugins\PluginScriptGenerator.cs" />
<Compile Include="Reporter.cs" />
<Compile Include="Updates\Events\UpdateDismissedEventArgs.cs" />
<Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
@@ -238,8 +240,8 @@
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\HardwareAcceleration.cs" />
<Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\UpdateAcceptedEventArgs.cs" />
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\Events\UpdateAcceptedEventArgs.cs" />
<Compile Include="Updates\Events\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Program.cs" />

View File

@@ -1,21 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDck", "TweetDck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "tests\UnitTests.csproj", "{A958FA7A-4A2C-42A7-BFA0-159343483F4E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|Any CPU.ActiveCfg = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.ActiveCfg = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Build.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Debug|x86.Deploy.0 = Debug|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|Any CPU.ActiveCfg = Release|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.ActiveCfg = Release|x86
{2389A7CD-E0D3-4706-8294-092929A33A2D}.Release|x86.Build.0 = Release|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|Any CPU.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.ActiveCfg = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Debug|x86.Build.0 = Debug|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|Any CPU.ActiveCfg = Release|x86
{A958FA7A-4A2C-42A7-BFA0-159343483F4E}.Release|x86.ActiveCfg = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

2
TweetDck.sln.DotSettings Normal file
View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters&gt;&lt;Filter ModuleMask="UnitTests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /&gt;&lt;/ExcludeFilters&gt;&lt;/data&gt;</s:String></wpf:ResourceDictionary>

View File

@@ -1,6 +1,6 @@
using System;
namespace TweetDck.Updates{
namespace TweetDck.Updates.Events{
class UpdateAcceptedEventArgs : EventArgs{
public readonly UpdateInfo UpdateInfo;

View File

@@ -1,6 +1,6 @@
using System;
namespace TweetDck.Updates{
namespace TweetDck.Updates.Events{
class UpdateCheckEventArgs : EventArgs{
public int EventId { get; private set; }
public bool UpdateAvailable { get; private set; }
@@ -12,4 +12,4 @@ namespace TweetDck.Updates{
LatestVersion = latestVersion;
}
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
namespace TweetDck.Updates.Events{
class UpdateDismissedEventArgs : EventArgs{
public readonly string VersionTag;
public UpdateDismissedEventArgs(string versionTag){
this.VersionTag = versionTag;
}
}
}

View File

@@ -9,7 +9,7 @@ using TweetDck.Core.Utils;
namespace TweetDck.Updates{
sealed partial class FormUpdateDownload : Form{
private const double BytesToMB = 1024.0*1024.0;
private const double BytesToKB = 1024.0;
public string InstallerPath{
get{
@@ -69,7 +69,7 @@ namespace TweetDck.Updates{
progressDownload.SetValueInstant(1000);
}
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" MB";
labelStatus.Text = (e.BytesReceived/BytesToKB).ToString("0.0", CultureInfo.CurrentCulture)+" kB";
}
else{
if (progressDownload.Style != ProgressBarStyle.Continuous){
@@ -77,7 +77,7 @@ namespace TweetDck.Updates{
}
progressDownload.SetValueInstant(e.ProgressPercentage*10);
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" / "+(e.TotalBytesToReceive/BytesToMB).ToString("0.0", CultureInfo.CurrentCulture)+" MB";
labelStatus.Text = (e.BytesReceived/BytesToKB).ToString("0.0", CultureInfo.CurrentCulture)+" / "+(e.TotalBytesToReceive/BytesToKB).ToString("0.0", CultureInfo.CurrentCulture)+" kB";
}
});
}

View File

@@ -5,14 +5,28 @@ using TweetDck.Core;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
using TweetDck.Resources;
using TweetDck.Updates.Events;
namespace TweetDck.Updates{
class UpdateHandler{
private static bool IsSystemSupported{
get{
return true; // Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
}
}
public UpdaterSettings Settings{
get{
return settings;
}
}
private readonly ChromiumWebBrowser browser;
private readonly FormBrowser form;
private readonly UpdaterSettings settings;
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
public event EventHandler<UpdateDismissedEventArgs> UpdateDismissed;
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
private int lastEventId;
@@ -23,18 +37,32 @@ namespace TweetDck.Updates{
this.settings = settings;
browser.FrameLoadEnd += browser_FrameLoadEnd;
browser.RegisterJsObject("$TDU", new Bridge(this));
browser.RegisterAsyncJsObject("$TDU", new Bridge(this));
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && BrowserUtils.IsTweetDeckWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "update.js");
Check(false);
}
}
public int Check(bool force){
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", force, ++lastEventId);
return lastEventId;
if (IsSystemSupported){
if (Program.UserConfig.EnableUpdateCheck || force){
string dismissedUpdate = force || settings.DismissedUpdate == null ? string.Empty : settings.DismissedUpdate;
browser.ExecuteScriptAsync("TDUF_runUpdateCheck", ++lastEventId, Program.VersionTag, dismissedUpdate, settings.AllowPreReleases);
return lastEventId;
}
return 0;
}
else if (settings.DismissedUpdate != "unsupported"){
browser.ExecuteScriptAsync("TDUF_displayNotification", "unsupported");
}
return -1;
}
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
@@ -43,6 +71,16 @@ namespace TweetDck.Updates{
}
}
private void TriggerUpdateDismissedEvent(UpdateDismissedEventArgs args){
form.InvokeSafe(() => {
settings.DismissedUpdate = args.VersionTag;
if (UpdateDismissed != null){
UpdateDismissed(this, args);
}
});
}
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
if (CheckFinished != null){
form.InvokeSafe(() => CheckFinished(this, args));
@@ -50,48 +88,16 @@ namespace TweetDck.Updates{
}
public class Bridge{
public string BrandName{
get{
return Program.BrandName;
}
}
public string VersionTag{
get{
return Program.VersionTag;
}
}
public bool UpdateCheckEnabled{
get{
return Program.UserConfig.EnableUpdateCheck;
}
}
public string DismissedVersionTag{
get{
return Program.UserConfig.DismissedUpdate ?? string.Empty;
}
}
public bool AllowPreReleases{
get{
return owner.settings.AllowPreReleases;
}
}
public bool IsSystemSupported{
get{
return Environment.OSVersion.Version >= new Version("6.1"); // 6.1 NT version = Windows 7
}
}
private readonly UpdateHandler owner;
public Bridge(UpdateHandler owner){
this.owner = owner;
}
public void TriggerUpdateCheck(){
owner.Check(false);
}
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId, isUpdateAvailable, latestVersion));
}
@@ -101,10 +107,7 @@ namespace TweetDck.Updates{
}
public void OnUpdateDismissed(string versionTag){
owner.form.InvokeSafe(() => {
Program.UserConfig.DismissedUpdate = versionTag;
Program.UserConfig.Save();
});
owner.TriggerUpdateDismissedEvent(new UpdateDismissedEventArgs(versionTag));
}
public void OpenBrowser(string url){

View File

@@ -1,5 +1,6 @@
namespace TweetDck.Updates{
class UpdaterSettings{
public bool AllowPreReleases { get; set; }
public string DismissedUpdate { get; set; }
}
}

View File

@@ -10,7 +10,7 @@
[Setup]
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
AppName={#MyAppName}
AppName={#MyAppName} Portable
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}

View File

@@ -12,7 +12,7 @@
[Setup]
AppId={{{#MyAppID}}
AppName={#MyAppName}
AppName={#MyAppName} Update
AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}

View File

@@ -0,0 +1,143 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using TweetDck.Core.Other.Settings.Export;
namespace UnitTests.Core.Settings{
[TestClass]
public class TestCombinedFileStream{
[TestMethod]
public void TestNoFiles(){
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_empty"))){
cfs.Flush();
}
Assert.IsTrue(File.Exists("cfs_empty"));
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
Assert.IsNull(cfs.ReadFile());
}
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_empty"))){
Assert.IsNull(cfs.SkipFile());
}
}
[TestMethod]
public void TestEmptyFiles(){
TestUtils.WriteText("cfs_input_empty_1", string.Empty);
TestUtils.WriteText("cfs_input_empty_2", string.Empty);
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_blank_files"))){
cfs.WriteFile("id1", "cfs_input_empty_1");
cfs.WriteFile("id2", "cfs_input_empty_2");
cfs.WriteFile("id2_clone", "cfs_input_empty_2");
cfs.Flush();
}
Assert.IsTrue(File.Exists("cfs_blank_files"));
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_blank_files"))){
CombinedFileStream.Entry entry1 = cfs.ReadFile();
string entry2key = cfs.SkipFile();
CombinedFileStream.Entry entry3 = cfs.ReadFile();
Assert.IsNull(cfs.ReadFile());
Assert.IsNull(cfs.SkipFile());
Assert.AreEqual("id1", entry1.KeyName);
Assert.AreEqual("id1", entry1.Identifier);
CollectionAssert.AreEqual(new string[0], entry1.KeyValue);
Assert.AreEqual("id2", entry2key);
Assert.AreEqual("id2_clone", entry3.KeyName);
Assert.AreEqual("id2_clone", entry3.Identifier);
CollectionAssert.AreEqual(new string[0], entry3.KeyValue);
entry1.WriteToFile("cfs_blank_file_1");
entry3.WriteToFile("cfs_blank_file_2");
TestUtils.DeleteFileOnExit("cfs_blank_file_1");
TestUtils.DeleteFileOnExit("cfs_blank_file_2");
}
Assert.IsTrue(File.Exists("cfs_blank_file_1"));
Assert.IsTrue(File.Exists("cfs_blank_file_2"));
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_1"));
Assert.AreEqual(string.Empty, TestUtils.ReadText("cfs_blank_file_2"));
}
[TestMethod]
public void TestTextFilesAndComplexKeys(){
TestUtils.WriteText("cfs_input_text_1", "Hello World!"+Environment.NewLine);
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_text_files"))){
cfs.WriteFile(new string[]{ "key1", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
cfs.WriteFile(new string[]{ "key2", "a", "bb", "ccc", "dddd" }, "cfs_input_text_1");
cfs.Flush();
}
Assert.IsTrue(File.Exists("cfs_text_files"));
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_text_files"))){
CombinedFileStream.Entry entry = cfs.ReadFile();
Assert.AreEqual("key2", cfs.SkipFile());
Assert.IsNull(cfs.ReadFile());
Assert.IsNull(cfs.SkipFile());
Assert.AreEqual("key1|a|bb|ccc|dddd", entry.Identifier);
Assert.AreEqual("key1", entry.KeyName);
CollectionAssert.AreEqual(new string[]{ "a", "bb", "ccc", "dddd" }, entry.KeyValue);
entry.WriteToFile("cfs_text_file_1");
TestUtils.DeleteFileOnExit("cfs_text_file_1");
}
Assert.IsTrue(File.Exists("cfs_text_file_1"));
Assert.AreEqual("Hello World!"+Environment.NewLine, TestUtils.ReadText("cfs_text_file_1"));
}
[TestMethod]
public void TestEntryWriteWithDirectory(){
if (Directory.Exists("cfs_directory")){
Directory.Delete("cfs_directory", true);
}
TestUtils.WriteText("cfs_input_dir_1", "test");
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.WriteFile("cfs_dir_test"))){
cfs.WriteFile("key1", "cfs_input_dir_1");
cfs.WriteFile("key2", "cfs_input_dir_1");
cfs.WriteFile("key3", "cfs_input_dir_1");
cfs.WriteFile("key4", "cfs_input_dir_1");
cfs.Flush();
}
Assert.IsTrue(File.Exists("cfs_dir_test"));
using(CombinedFileStream cfs = new CombinedFileStream(TestUtils.ReadFile("cfs_dir_test"))){
try{
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", false);
Assert.Fail("WriteToFile did not trigger an exception.");
}catch(DirectoryNotFoundException){}
cfs.ReadFile().WriteToFile("cfs_directory/cfs_dir_test_file", true);
cfs.ReadFile().WriteToFile("cfs_dir_test_file", true);
cfs.ReadFile().WriteToFile("cfs_dir_test_file.txt", true);
TestUtils.DeleteFileOnExit("cfs_dir_test_file");
TestUtils.DeleteFileOnExit("cfs_dir_test_file.txt");
}
Assert.IsTrue(Directory.Exists("cfs_directory"));
Assert.IsTrue(File.Exists("cfs_directory/cfs_dir_test_file"));
Assert.IsTrue(File.Exists("cfs_dir_test_file"));
Assert.IsTrue(File.Exists("cfs_dir_test_file.txt"));
Assert.AreEqual("test", TestUtils.ReadText("cfs_directory/cfs_dir_test_file"));
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file"));
Assert.AreEqual("test", TestUtils.ReadText("cfs_dir_test_file.txt"));
Directory.Delete("cfs_directory", true);
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDck.Core.Utils;
namespace UnitTests.Core.Utils{
[TestClass]
public class TestBrowserUtils{
[TestMethod]
public void TestGetFileNameFromUrl(){
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html"));
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?"));
Assert.AreEqual("index.html", BrowserUtils.GetFileNameFromUrl("http://test.com/index.html?param1=abc&param2=false"));
Assert.AreEqual("index", BrowserUtils.GetFileNameFromUrl("http://test.com/index"));
Assert.AreEqual("index.", BrowserUtils.GetFileNameFromUrl("http://test.com/index."));
Assert.IsNull(BrowserUtils.GetFileNameFromUrl("http://test.com/"));
}
}
}

View File

@@ -0,0 +1,157 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using TweetDck.Core.Utils;
namespace UnitTests.Core.Utils{
[TestClass]
public class TestCommandLineArgs{
[TestMethod]
public void TestEmpty(){
CommandLineArgs args = new CommandLineArgs();
Assert.AreEqual(0, args.Count);
Assert.AreEqual(string.Empty, args.ToString());
Assert.IsFalse(args.HasFlag("x"));
Assert.IsFalse(args.HasValue("x"));
Assert.AreEqual("default", args.GetValue("x", "default"));
args.RemoveFlag("x");
args.RemoveValue("x");
var dict = new Dictionary<string, string>();
args.ToDictionary(dict);
Assert.AreEqual(0, dict.Count);
}
[TestMethod]
public void TestFlags(){
CommandLineArgs args = new CommandLineArgs();
args.AddFlag("my_test_flag_1");
args.AddFlag("my_test_flag_2");
args.AddFlag("aAaAa");
Assert.IsFalse(args.HasValue("aAaAa"));
Assert.AreEqual(3, args.Count);
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
Assert.IsTrue(args.HasFlag("aaaaa"));
Assert.IsTrue(args.HasFlag("AAAAA"));
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa", args.ToString());
args.RemoveFlag("Aaaaa");
Assert.AreEqual(2, args.Count);
Assert.IsTrue(args.HasFlag("my_test_flag_1"));
Assert.IsTrue(args.HasFlag("my_test_flag_2"));
Assert.IsFalse(args.HasFlag("aaaaa"));
Assert.AreEqual("my_test_flag_1 my_test_flag_2", args.ToString());
}
[TestMethod]
public void TestValues(){
CommandLineArgs args = new CommandLineArgs();
args.SetValue("test_value", "My Test Value");
args.SetValue("aAaAa", "aaaaa");
Assert.IsFalse(args.HasFlag("aAaAa"));
Assert.AreEqual(2, args.Count);
Assert.IsTrue(args.HasValue("test_value"));
Assert.IsTrue(args.HasValue("aaaaa"));
Assert.IsTrue(args.HasValue("AAAAA"));
Assert.AreEqual("My Test Value", args.GetValue("test_value", string.Empty));
Assert.AreEqual("aaaaa", args.GetValue("aaaaa", string.Empty));
Assert.AreEqual("test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
args.RemoveValue("Aaaaa");
Assert.AreEqual(1, args.Count);
Assert.IsTrue(args.HasValue("test_value"));
Assert.IsFalse(args.HasValue("aaaaa"));
Assert.AreEqual("test_value \"My Test Value\"", args.ToString());
}
[TestMethod]
public void TestFlagAndValueMix(){
CommandLineArgs args = new CommandLineArgs();
args.AddFlag("my_test_flag_1");
args.AddFlag("my_test_flag_2");
args.AddFlag("aAaAa");
args.SetValue("test_value", "My Test Value");
args.SetValue("aAaAa", "aaaaa");
Assert.AreEqual(5, args.Count);
Assert.IsTrue(args.HasFlag("aaaaa"));
Assert.IsTrue(args.HasValue("aaaaa"));
Assert.AreEqual("my_test_flag_1 my_test_flag_2 aaaaa test_value \"My Test Value\" aaaaa \"aaaaa\"", args.ToString());
var dict = new Dictionary<string, string>();
args.ToDictionary(dict); // loses 'aaaaa' flag
Assert.AreEqual(4, dict.Count);
Assert.AreEqual("1", dict["my_test_flag_1"]);
Assert.AreEqual("1", dict["my_test_flag_2"]);
Assert.AreEqual("My Test Value", dict["test_value"]);
Assert.AreEqual("aaaaa", dict["aaaaa"]);
}
[TestMethod]
public void TestClone(){
CommandLineArgs args = new CommandLineArgs();
args.AddFlag("my_test_flag_1");
args.AddFlag("my_test_flag_2");
args.AddFlag("aAaAa");
args.SetValue("test_value", "My Test Value");
args.SetValue("aAaAa", "aaaaa");
CommandLineArgs clone = args.Clone();
args.RemoveFlag("aaaaa");
args.RemoveValue("aaaaa");
clone.RemoveFlag("my_test_flag_1");
clone.RemoveFlag("my_test_flag_2");
clone.RemoveValue("test_value");
Assert.AreEqual(3, args.Count);
Assert.AreEqual(2, clone.Count);
Assert.AreEqual("my_test_flag_1 my_test_flag_2 test_value \"My Test Value\"", args.ToString());
Assert.AreEqual("aaaaa aaaaa \"aaaaa\"", clone.ToString());
}
[TestMethod]
public void TestEmptyStringArray(){
CommandLineArgs args;
args = CommandLineArgs.FromStringArray('-', new string[0]);
Assert.AreEqual(0, args.Count);
args = CommandLineArgs.FromStringArray('-', new string[]{ "", "+fail", "@nope" });
Assert.AreEqual(0, args.Count);
}
[TestMethod]
public void TestValidStringArray(){
CommandLineArgs args;
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag1", "-flag2", "-FLAG3" });
Assert.AreEqual(3, args.Count);
Assert.IsTrue(args.HasFlag("-flag1"));
Assert.IsTrue(args.HasFlag("-flag2"));
Assert.IsTrue(args.HasFlag("-flag3"));
args = CommandLineArgs.FromStringArray('-', new string[]{ "-flag", "-value", "Here is some text!" });
Assert.AreEqual(2, args.Count);
Assert.IsTrue(args.HasFlag("-flag"));
Assert.IsTrue(args.HasValue("-value"));
Assert.AreEqual("Here is some text!", args.GetValue("-value", string.Empty));
}
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TweetDck.Core.Utils;
namespace UnitTests.Core.Utils{
[TestClass]
public class TestCommandLineArgsParser{
[TestMethod]
public void TestEmptyString(){
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments("").Count);
Assert.AreEqual(0, CommandLineArgsParser.ReadCefArguments(" ").Count);
}
[TestMethod]
public void TestValidString(){
CommandLineArgs args = CommandLineArgsParser.ReadCefArguments("--aaa --bbb --first-value=123 --SECOND-VALUE=\"a b c d e\" --ccc");
// cef has no flags, flag arguments have a value of 1
// the processing removes all dashes in front of each key
Assert.AreEqual(5, args.Count);
Assert.IsTrue(args.HasValue("aaa"));
Assert.IsTrue(args.HasValue("bbb"));
Assert.IsTrue(args.HasValue("ccc"));
Assert.IsTrue(args.HasValue("first-value"));
Assert.IsTrue(args.HasValue("second-value"));
Assert.AreEqual("1", args.GetValue("aaa", string.Empty));
Assert.AreEqual("1", args.GetValue("bbb", string.Empty));
Assert.AreEqual("1", args.GetValue("ccc", string.Empty));
Assert.AreEqual("123", args.GetValue("first-value", string.Empty));
Assert.AreEqual("a b c d e", args.GetValue("second-value", string.Empty));
}
}
}

View File

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

65
tests/TestUtils.cs Normal file
View File

@@ -0,0 +1,65 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests{
public static class TestUtils{
private static readonly HashSet<string> CreatedFiles = new HashSet<string>();
public static void WriteText(string file, string text){
DeleteFileOnExit(file);
File.WriteAllText(file, text, Encoding.UTF8);
}
public static void WriteLines(string file, IEnumerable<string> lines){
DeleteFileOnExit(file);
File.WriteAllLines(file, lines, Encoding.UTF8);
}
public static FileStream WriteFile(string file){
DeleteFileOnExit(file);
return new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
}
public static string ReadText(string file){
try{
return File.ReadAllText(file, Encoding.UTF8);
}catch(Exception){
return string.Empty;
}
}
public static IEnumerable<string> ReadLines(string file){
try{
return File.ReadLines(file, Encoding.UTF8);
}catch(Exception){
return Enumerable.Empty<string>();
}
}
public static FileStream ReadFile(string file){
return new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None);
}
public static void DeleteFileOnExit(string file){
CreatedFiles.Add(file);
}
[TestClass]
public static class Cleanup{
[AssemblyCleanup]
public static void DeleteFilesOnExit(){
foreach(string file in CreatedFiles){
try{
File.Delete(file);
}catch(Exception){
// ignore
}
}
}
}
}
}

90
tests/UnitTests.csproj Normal file
View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A958FA7A-4A2C-42A7-BFA0-159343483F4E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests</RootNamespace>
<AssemblyName>UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Debug\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Core\Settings\TestCombinedFileStream.cs" />
<Compile Include="Core\Utils\TestBrowserUtils.cs" />
<Compile Include="Core\Utils\TestCommandLineArgs.cs" />
<Compile Include="Core\Utils\TestCommandLineArgsParser.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestUtils.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TweetDck.csproj">
<Project>{2389a7cd-e0d3-4706-8294-092929a33a2d}</Project>
<Name>TweetDck</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>