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

Compare commits

..

74 Commits
1.0 ... 1.2

Author SHA1 Message Date
15a39de939 Update version to 1.2 2016-04-26 18:51:45 +02:00
4142206b65 Fix icon in Volume Mixer
Closes #20
2016-04-26 16:04:02 +02:00
6105658602 Temporarily re-add default sound notification, but make it work with Mute function 2016-04-26 15:49:24 +02:00
a6e40be79e Fix maximization resetting restore location and size 2016-04-26 15:19:54 +02:00
95fba6a99a Fix license file copying in post build events 2016-04-26 11:59:47 +02:00
aa2e4c9845 Implement multiple tray options and refactor tray icon handling 2016-04-26 02:41:19 +02:00
f4cfc40244 Redo locations and margins in settings Form 2016-04-26 01:01:16 +02:00
6c50bdba06 Rename 'Update Notifications' setting to 'Check for Updates' 2016-04-26 00:44:49 +02:00
1f6fc491ef Add a WIP sound notification hook 2016-04-26 00:43:44 +02:00
1c3a7b6ce7 Implement holding Ctrl to open clicked links in browser 2016-04-25 18:07:54 +02:00
b04e260fb7 Fix focus stealing issues when showing or hiding the notification Form 2016-04-25 17:58:10 +02:00
4a66486e1a Rename extendFunction to appendToFunction, and add prependToFunction to code.js 2016-04-25 17:01:56 +02:00
a335aa037a Fix clicking on nested/complex links in notification.js not triggering the hooks 2016-04-25 16:17:28 +02:00
dd4c89b9dd Add notification Form title changing with enqueued tweets 2016-04-25 16:12:58 +02:00
ddbf6da061 Fix double-clicking maximizing notification Form by setting MaximizeBox to false (thanks Microsoft...) 2016-04-25 16:06:07 +02:00
f85e0030a7 Add Mute Notifications setting (required changes to notification focus handling)
Closes #13
2016-04-25 15:51:14 +02:00
c0271d273f Remove unused fontSizeClasses from code.js 2016-04-25 12:49:57 +02:00
2a1dc8beab Bypass t.co links in context menu and hide url options for # links 2016-04-24 22:13:48 +02:00
c5b3bc1a0b Fix context menu not running any actions in notification Form 2016-04-24 22:04:41 +02:00
316b1db3f6 Force word-break:break-all on expanded links 2016-04-24 20:51:30 +02:00
49fa7626b6 Expand shortened links on hover
Closes #12
2016-04-24 20:47:10 +02:00
24edcc3402 Rewrite notification system to improve reliability and future extensibility
Closes #18
2016-04-24 16:37:27 +02:00
c53a1cbd01 Fix clicking on "Followed by:" users opening the browser 2016-04-24 14:53:11 +02:00
c138b13d01 Rewrite theme change handling 2016-04-24 14:28:39 +02:00
acc9b58660 Remove getFontSizeClass from code.js 2016-04-24 14:19:20 +02:00
b94a6acee6 Add address check to notification form before running notification.js 2016-04-24 14:15:04 +02:00
b2892cc834 Rewrite font size handling 2016-04-24 14:10:29 +02:00
82a1e17b1d Add extendFunction and refactor window.TD in code.js 2016-04-24 14:04:52 +02:00
82a3cd8df2 Add a simple context menu to the tray icon (Restore, Close) 2016-04-23 20:59:23 +02:00
b39a3a05fe Move update notification code to update.js and make the notification display without needing the app div 2016-04-23 17:54:15 +02:00
f4c7eb14ec Add ScriptLoader.LoadResources with unlimited parameters 2016-04-23 17:53:01 +02:00
92ac138183 Make sure tray icon brings the window to front
Closes #14
2016-04-23 17:06:59 +02:00
b0fe8cf53e Rename TweetD*ck settings option on the website for clarity 2016-04-23 17:00:23 +02:00
016e403309 Fix LockManager to check process name before reporting, and cleanup Close/Dispose code
Closes #17
2016-04-23 16:51:11 +02:00
4cef0fb60d Fix clipboard text formatting
Closes #19
2016-04-23 16:29:51 +02:00
d53eff6043 Update version to 1.1.1 2016-04-17 17:42:14 +02:00
f064912ac9 Prevent notifications from stealing focus from the client app 2016-04-17 17:42:00 +02:00
f9f0677da3 Fix broken pop-up notifications 2016-04-17 17:41:46 +02:00
16180fd5d6 Update version to 1.1 2016-04-17 16:49:54 +02:00
462aebb115 Fix notification height and sent DMs showing up in notifications 2016-04-17 16:49:49 +02:00
0cb3bd9cc6 Add Cef.Shutdown before running the updater 2016-04-17 16:49:12 +02:00
2ef4f28740 Move Update form labels to the left 2016-04-17 16:19:15 +02:00
b02fb0934f Add version tag to About form 2016-04-17 16:10:10 +02:00
de363c982f Add update downloader and installer 2016-04-17 16:06:56 +02:00
f0132f59e5 Add ControlExtensions.MoveToCenter 2016-04-17 15:44:04 +02:00
dc32285454 Add ControlExtensions with Form.InvokeSafe and ProgressBar.SetValueInstant 2016-04-17 12:49:53 +02:00
e170172870 Add BrowserUtils.GetFileNameFromUrl and use it in ContextMenuBase 2016-04-17 01:35:27 +02:00
eba47a8196 "Fix" RichTextLabel being a fucking idiot and not working with URLs properly
Closes #10
2016-04-17 00:48:11 +02:00
907543a7cf Add WIP update notifications, currently no downloading 2016-04-16 23:22:23 +02:00
e8dea023ac Rename links and references in code to match new repository setup 2016-04-16 16:38:46 +02:00
8cfb6ab4f5 Merge branch 'master' of https://github.com/chylex/TweetDuck 2016-04-16 16:26:34 +02:00
c64d58143e Rename the project and solution to TweetDck 2016-04-16 16:20:33 +02:00
991681e186 Rename LICENSE to LICENSE.md 2016-04-16 16:16:30 +02:00
3c17c36f4f Move log file path to a separate property and make sure it always has the correct path 2016-04-16 14:39:48 +02:00
707b4b4ba2 Update the example notification (change my username) 2016-04-16 14:25:29 +02:00
413c7564aa Add VC++ 2013 redist libraries 2016-04-16 14:10:40 +02:00
453bf2dd82 Fix notifications removing DM contents 2016-04-16 13:56:29 +02:00
5597d2aed0 Ignore notifications with 0 characters 2016-04-16 03:03:48 +02:00
3c43211b25 Make sure ScriptLoader uses correct root path 2016-04-16 02:02:10 +02:00
ee87841ec2 Open browser when clicking on a "gif" (actually mp4) 2016-04-15 23:24:21 +02:00
9e5a39e9fc Add Save image as... option to context menu 2016-04-15 23:04:28 +02:00
8c21011ac7 Add BrowserUtils with header getters and OpenExternalBrowser method 2016-04-15 22:42:35 +02:00
5f5013f021 Add ContextMenuBase that handles links and media (open in browser, copy url) 2016-04-15 18:44:48 +02:00
fe093475dc Rename ContextMenuHandler to ContextMenuBrowser and add ContextMenuNotification 2016-04-15 17:30:33 +02:00
1c4e03ebea Make the notification timer config affect example notification, and fix its height 2016-04-15 16:05:09 +02:00
860b740db9 Add upgrade mechanism for config file 2016-04-15 16:02:17 +02:00
bd54da85a8 Add a Display Notification Timer option 2016-04-15 15:57:22 +02:00
86165b3529 Add Minimize to Tray option and improve window state and position saving 2016-04-15 15:06:42 +02:00
09d028a3af Fix minimizing issues again but properly 2016-04-15 14:39:38 +02:00
1cb261cb6f Merge branch 'master' of https://github.com/chylex/TweetDick 2016-04-15 14:35:14 +02:00
82934878cc Fix minimizing causing issues with saved window position 2016-04-15 14:35:10 +02:00
996c460f57 Create README.md 2016-04-15 14:05:45 +02:00
33e704f1ab Add support for loading config files across all TweetD*ck variations 2016-04-15 13:23:59 +02:00
2c3849bc43 Change namespace to TweetDck 2016-04-15 13:23:40 +02:00
48 changed files with 1661 additions and 426 deletions

View File

@@ -3,7 +3,7 @@ using System.Diagnostics;
using System.IO;
using System.Threading;
namespace TweetDick.Configuration{
namespace TweetDck.Configuration{
class LockManager{
private readonly string file;
private FileStream lockStream;
@@ -54,7 +54,15 @@ namespace TweetDick.Configuration{
int pid = BitConverter.ToInt32(bytes,0);
try{
lockingProcess = Process.GetProcessById(pid);
Process foundProcess = Process.GetProcessById(pid);
using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.ProcessName == currentProcess.ProcessName){
lockingProcess = foundProcess;
}
currentProcess.Close();
}
}catch(ArgumentException){}
return lockingProcess == null && CreateLockFile();
@@ -76,7 +84,6 @@ namespace TweetDick.Configuration{
public void Unlock(){
if (lockStream != null){
lockStream.Close();
lockStream.Dispose();
File.Delete(file);
@@ -96,7 +103,7 @@ namespace TweetDick.Configuration{
}
if (lockingProcess.HasExited){
lockingProcess.Close();
lockingProcess.Dispose();
lockingProcess = null;
return true;
}

View File

@@ -3,13 +3,17 @@ using System.Drawing;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using TweetDick.Core.Handling;
using TweetDck.Core;
using TweetDck.Core.Handling;
namespace TweetDick.Configuration{
namespace TweetDck.Configuration{
[Serializable]
sealed class UserConfig{
private static readonly IFormatter Formatter = new BinaryFormatter();
private static readonly IFormatter Formatter = new BinaryFormatter(){
Binder = new SerializationCompatibilityHandler()
};
private const int CurrentFileVersion = 1;
// START OF CONFIGURATION
@@ -19,6 +23,7 @@ namespace TweetDick.Configuration{
public bool IsMaximized { get; set; }
public Point WindowLocation { get; set; }
public Size WindowSize { get; set; }
public bool DisplayNotificationTimer { get; set; }
public TweetNotification.Duration NotificationDuration { get; set; }
public TweetNotification.Position NotificationPosition { get; set; }
@@ -26,6 +31,9 @@ namespace TweetDick.Configuration{
public int NotificationEdgeDistance { get; set; }
public int NotificationDisplay { get; set; }
public bool EnableUpdateCheck { get; set; }
public string DismissedUpdate { get; set; }
public bool IsCustomWindowLocationSet{
get{
return WindowLocation.X != -32000 && WindowLocation.X != 32000;
@@ -38,20 +46,81 @@ namespace TweetDick.Configuration{
}
}
public bool MuteNotifications{
get{
return muteNotifications;
}
set{
if (muteNotifications == value)return;
muteNotifications = value;
if (MuteToggled != null){
MuteToggled(this,new EventArgs());
}
}
}
public TrayIcon.Behavior TrayBehavior{
get{
return trayBehavior;
}
set{
if (trayBehavior == value)return;
trayBehavior = value;
if (TrayBehaviorChanged != null){
TrayBehaviorChanged(this,new EventArgs());
}
}
}
// END OF CONFIGURATION
[field:NonSerialized]
public event EventHandler MuteToggled;
[field:NonSerialized]
public event EventHandler TrayBehaviorChanged;
[NonSerialized]
private string file;
private int fileVersion;
private bool muteNotifications;
private TrayIcon.Behavior trayBehavior;
private UserConfig(string file){
this.file = file;
IsMaximized = true;
DisplayNotificationTimer = true;
WindowLocation = new Point(-32000,-32000);
NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = new Point(-32000,-32000);
NotificationEdgeDistance = 8;
EnableUpdateCheck = true;
}
private void UpgradeFile(){
if (fileVersion == CurrentFileVersion){
return;
}
// if outdated, cycle through all versions
if (fileVersion == 0){
DisplayNotificationTimer = true;
EnableUpdateCheck = true;
++fileVersion;
}
// update the version
fileVersion = CurrentFileVersion;
Save();
}
public bool Save(){
@@ -89,6 +158,10 @@ namespace TweetDick.Configuration{
}
}
if (config != null){
config.UpgradeFile();
}
break;
}catch(FileNotFoundException){
}catch(Exception e){
@@ -102,5 +175,18 @@ namespace TweetDick.Configuration{
private static string GetBackupFile(string file){
return file+".bak";
}
private class SerializationCompatibilityHandler : SerializationBinder{
public override Type BindToType(string assemblyName, string typeName){
#if DUCK
assemblyName = assemblyName.Replace("TweetDick","TweetDuck");
#else
assemblyName = assemblyName.Replace("TweetDuck","TweetDick");
#endif
typeName = typeName.Replace("TweetDick","TweetDck");
return Type.GetType(string.Format("{0}, {1}",typeName,assemblyName));
}
}
}
}

View File

@@ -4,4 +4,5 @@
<package id="cef.redist.x86" version="3.2623.1396" targetFramework="net40-Client" />
<package id="CefSharp.Common" version="49.0.0-pre02" targetFramework="net40-Client" />
<package id="CefSharp.WinForms" version="49.0.0-pre02" targetFramework="net40-Client" />
<package id="Microsoft.VC120.CRT.JetBrains" version="12.0.21005.2" targetFramework="net40-Client" />
</packages>

View File

@@ -0,0 +1,32 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TweetDck.Core.Controls{
static class ControlExtensions{
public static void InvokeSafe(this Form form, Action func){
if (form.InvokeRequired){
form.Invoke(func);
}
else{
func();
}
}
public static void MoveToCenter(this Form targetForm, Form parentForm){
targetForm.Location = new Point(parentForm.Location.X+parentForm.Width/2-targetForm.Width/2,parentForm.Location.Y+parentForm.Height/2-targetForm.Height/2);
}
public static void SetValueInstant(this ProgressBar bar, int value){
if (value == bar.Maximum){
bar.Value = value;
bar.Value = value-1;
bar.Value = value;
}
else{
bar.Value = value+1;
bar.Value = value;
}
}
}
}

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Core.Controls {
namespace TweetDck.Core.Controls {
partial class FlatProgressBar {
/// <summary>
/// Required designer variable.

View File

@@ -1,7 +1,7 @@
using System.Drawing;
using System.Windows.Forms;
namespace TweetDick.Core.Controls{
namespace TweetDck.Core.Controls{
public partial class FlatProgressBar : ProgressBar{
private SolidBrush brush;
@@ -11,15 +11,7 @@ namespace TweetDick.Core.Controls{
}
public void SetValueInstant(int value){
if (value == Maximum){
Value = value;
Value = value-1;
Value = value;
}
else{
Value = value+1;
Value = value;
}
ControlExtensions.SetValueInstant(this,value);
}
protected override void OnPaint(PaintEventArgs e){

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Core.Controls {
namespace TweetDck.Core.Controls {
partial class RichTextLabel {
/// <summary>
/// Required designer variable.

View File

@@ -1,7 +1,7 @@
using System;
using System.Windows.Forms;
namespace TweetDick.Core.Controls{
namespace TweetDck.Core.Controls{
public partial class RichTextLabel : RichTextBox{
/// <summary>
/// Wraps the body of a RTF formatted string with default tags and formatting.

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Core {
namespace TweetDck.Core {
sealed partial class FormBrowser {
/// <summary>
/// Required designer variable.
@@ -23,23 +23,32 @@
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormBrowser));
this.trayIcon = new TweetDck.Core.TrayIcon();
this.SuspendLayout();
//
// trayIcon
//
this.trayIcon.Visible = false;
//
// FormBrowser
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Icon = TweetDick.Properties.Resources.icon;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Icon = ((System.Drawing.Icon)(TweetDck.Properties.Resources.ResourceManager.GetObject("icon")));
this.Location = new System.Drawing.Point(-32000, -32000);
this.Name = "FormBrowser";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormBrowser_FormClosing);
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
this.ResumeLayout(false);
}
#endregion
private TrayIcon trayIcon;
}
}

View File

@@ -1,15 +1,17 @@
using System.Windows.Forms;
using CefSharp.WinForms;
using System;
using System;
using System.Linq;
using TweetDick.Configuration;
using System.Windows.Forms;
using CefSharp;
using TweetDick.Core.Handling;
using TweetDick.Core.Other;
using System.Drawing;
using TweetDick.Resources;
using CefSharp.WinForms;
using TweetDck.Configuration;
using TweetDck.Core.Handling;
using TweetDck.Core.Other;
using TweetDck.Resources;
using TweetDck.Core.Utils;
using TweetDck.Core.Controls;
using System.ComponentModel;
namespace TweetDick.Core{
namespace TweetDck.Core{
sealed partial class FormBrowser : Form{
private static UserConfig Config{
get{
@@ -17,6 +19,8 @@ namespace TweetDick.Core{
}
}
public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser;
private readonly TweetDeckBridge bridge;
private readonly FormNotification notification;
@@ -25,6 +29,8 @@ namespace TweetDick.Core{
private FormAbout currentFormAbout;
private bool isLoaded;
private FormWindowState prevState;
public FormBrowser(){
InitializeComponent();
@@ -32,7 +38,7 @@ namespace TweetDick.Core{
bridge = new TweetDeckBridge(this);
browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){ MenuHandler = new ContextMenuHandler(this) };
browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){ MenuHandler = new ContextMenuBrowser(this) };
browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
browser.RegisterJsObject("$TD",bridge);
@@ -41,22 +47,19 @@ namespace TweetDick.Core{
Disposed += (sender, args) => browser.Dispose();
trayIcon.ClickRestore += trayIcon_ClickRestore;
trayIcon.ClickClose += trayIcon_ClickClose;
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
UpdateTrayIcon();
notification = new FormNotification(this,bridge,true){ CanMoveWindow = () => false };
notification.Show();
}
protected override void WndProc(ref Message m){
FormWindowState prevState = WindowState;
base.WndProc(ref m);
if (prevState != WindowState && m.Msg == 0x0014){ // WM_ERASEBKGND
FormBrowser_WindowStateChanged(this,new EventArgs());
}
}
private void ShowChildForm(Form form){
form.Show(this);
form.Location = new Point(Location.X+Width/2-form.Width/2,Location.Y+Height/2-form.Height/2);
form.MoveToCenter(this);
}
// window setup
@@ -74,9 +77,14 @@ namespace TweetDick.Core{
WindowState = FormWindowState.Maximized;
}
prevState = WindowState;
isLoaded = true;
}
private void UpdateTrayIcon(){
trayIcon.Visible = Config.TrayBehavior != TrayIcon.Behavior.Disabled;
}
// active event handlers
private void Browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
@@ -88,38 +96,80 @@ namespace TweetDick.Core{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain){
string js = ScriptLoader.LoadResource("code.js");
if (js != null){
foreach(string js in ScriptLoader.LoadResources("code.js","update.js").Where(js => js != null)){
browser.ExecuteScriptAsync(js);
}
}
}
private void FormBrowser_Resize(object sender, EventArgs e){
if (!isLoaded)return;
if (WindowState != prevState){
prevState = WindowState;
if (WindowState == FormWindowState.Minimized){
if (Config.TrayBehavior == TrayIcon.Behavior.MinimizeToTray){
Hide(); // hides taskbar too?! welp that works I guess
}
}
else{
FormBrowser_ResizeEnd(sender,e);
}
}
}
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
if (!isLoaded)return;
Config.WindowLocation = Location;
Config.WindowSize = Size;
Config.Save();
if (Location.X != -32000){
Config.IsMaximized = WindowState == FormWindowState.Maximized;
if (WindowState == FormWindowState.Normal){
Config.WindowLocation = Location;
Config.WindowSize = Size;
}
Config.Save();
}
}
private void FormBrowser_WindowStateChanged(object sender, EventArgs e){
private void FormBrowser_FormClosing(object sender, FormClosingEventArgs e){
if (!isLoaded)return;
Config.IsMaximized = WindowState != FormWindowState.Normal;
FormBrowser_ResizeEnd(sender,e);
if (Config.TrayBehavior == TrayIcon.Behavior.CloseToTray && trayIcon.Visible && e.CloseReason == CloseReason.UserClosing){
Hide(); // hides taskbar too?! welp that works I guess
e.Cancel = true;
}
}
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
if (!isLoaded)return;
UpdateTrayIcon();
}
private void trayIcon_ClickRestore(object sender, EventArgs e){
if (!isLoaded)return;
isLoaded = false;
Show();
SetupWindow();
Activate();
UpdateTrayIcon();
}
private void trayIcon_ClickClose(object sender, EventArgs e){
if (!isLoaded)return;
trayIcon.Visible = false; // checked in FormClosing event
Close();
}
// callback handlers
public void InvokeSafe(Action func){
if (InvokeRequired){
Invoke(func);
}
else{
func();
}
ControlExtensions.InvokeSafe(this,func);
}
public void OpenSettings(){
@@ -127,8 +177,21 @@ namespace TweetDick.Core{
currentFormSettings.BringToFront();
}
else{
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
currentFormSettings = new FormSettings(this);
currentFormSettings.FormClosed += (sender, args) => currentFormSettings = null;
currentFormSettings.FormClosed += (sender, args) => {
currentFormSettings = null;
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
Config.DismissedUpdate = string.Empty;
Config.Save();
browser.ExecuteScriptAsync("TDGF_runUpdateCheck",new object[0]);
}
};
ShowChildForm(currentFormSettings);
}
}
@@ -147,5 +210,28 @@ namespace TweetDick.Core{
public void OnTweetPopup(TweetNotification tweet){
notification.ShowNotification(tweet);
}
public void OnTweetSound(){
}
public void BeginUpdateProcess(string versionTag, string downloadUrl){
Hide();
FormUpdateDownload downloadForm = new FormUpdateDownload(new UpdateInfo(versionTag,downloadUrl));
downloadForm.MoveToCenter(this);
downloadForm.ShowDialog();
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
UpdateInstallerPath = downloadForm.InstallerPath;
Close();
}
else if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Manual){
Close();
}
else{
Show();
}
}
}
}

View File

@@ -1,4 +1,6 @@
namespace TweetDick.Core {
using TweetDck.Core.Controls;
namespace TweetDck.Core {
sealed partial class FormNotification {
/// <summary>
/// Required designer variable.
@@ -26,7 +28,7 @@
this.components = new System.ComponentModel.Container();
this.panelBrowser = new System.Windows.Forms.Panel();
this.timerProgress = new System.Windows.Forms.Timer(this.components);
this.progressBarTimer = new TweetDick.Core.Controls.FlatProgressBar();
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
this.SuspendLayout();
//
// panelBrowser
@@ -66,6 +68,8 @@
this.Controls.Add(this.panelBrowser);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Location = new System.Drawing.Point(-32000, -32000);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormNotification";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;

View File

@@ -1,14 +1,14 @@
using System.Windows.Forms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using System.Drawing;
using System;
using System.Collections.Generic;
using TweetDick.Core.Handling;
using TweetDick.Configuration;
using TweetDick.Resources;
using TweetDck.Configuration;
using TweetDck.Core.Handling;
using TweetDck.Resources;
namespace TweetDick.Core{
namespace TweetDck.Core{
sealed partial class FormNotification : Form{
public Func<bool> CanMoveWindow = () => true;
@@ -21,6 +21,12 @@ namespace TweetDick.Core{
private readonly string notificationJS;
protected override bool ShowWithoutActivation{
get{
return true;
}
}
public FormNotification(Form owner, TweetDeckBridge bridge, bool autoHide){
InitializeComponent();
@@ -33,7 +39,7 @@ namespace TweetDick.Core{
notificationJS = ScriptLoader.LoadResource("notification.js");
browser = new ChromiumWebBrowser("about:blank"){ MenuHandler = new MenuHandlerEmpty() };
browser = new ChromiumWebBrowser("about:blank"){ MenuHandler = new ContextMenuNotification() };
browser.FrameLoadEnd += Browser_FrameLoadEnd;
if (bridge != null){
@@ -42,17 +48,16 @@ namespace TweetDick.Core{
panelBrowser.Controls.Add(browser);
if (autoHide){
Program.UserConfig.MuteToggled += Config_MuteToggled;
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
}
Disposed += (sender, args) => browser.Dispose();
}
public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && notificationJS != null){
browser.ExecuteScriptAsync(notificationJS);
}
}
protected override void WndProc(ref Message m){
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
return;
@@ -61,24 +66,77 @@ namespace TweetDick.Core{
base.WndProc(ref m);
}
public void ShowNotification(TweetNotification notification){
MoveToVisibleLocation();
// event handlers
tweetQueue.Enqueue(notification);
private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position))return;
if (!timerProgress.Enabled){
LoadNextNotification();
timeLeft -= timerProgress.Interval;
progressBarTimer.SetValueInstant((int)Math.Min(1000,Math.Round(1050.0*(totalTime-timeLeft)/totalTime)));
if (timeLeft <= 0){
if (tweetQueue.Count > 0){
LoadNextNotification();
}
else if (autoHide){
HideNotification();
}
}
}
public void ShowNotificationForSettings(bool resetAnimation){
private void Config_MuteToggled(object sender, EventArgs e){
if (Program.UserConfig.MuteNotifications){
HideNotification();
}
else{
if (tweetQueue.Count > 0){
MoveToVisibleLocation();
LoadNextNotification();
}
}
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && notificationJS != null && browser.Address != "about:blank"){
browser.ExecuteScriptAsync(notificationJS);
}
}
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
if (e.CloseReason == CloseReason.UserClosing){
HideNotification();
tweetQueue.Clear();
e.Cancel = true;
}
}
// notification methods
public void ShowNotification(TweetNotification notification){
if (Program.UserConfig.MuteNotifications){
tweetQueue.Enqueue(notification);
}
else{
MoveToVisibleLocation();
tweetQueue.Enqueue(notification);
UpdateTitle();
if (!timerProgress.Enabled){
LoadNextNotification();
}
}
}
public void ShowNotificationForSettings(bool reset){
if (browser.Address == "about:blank"){
browser.Load("about:blank"); // required, otherwise shit breaks
browser.LoadHtml(TweetNotification.ExampleTweet.GenerateHtml(),"http://tweetdeck.twitter.com/");
resetAnimation = true;
reset = true;
}
if (resetAnimation){
if (reset){
browser.LoadHtml(TweetNotification.ExampleTweet.GenerateHtml(),"http://tweetdeck.twitter.com/");
totalTime = timeLeft = TweetNotification.ExampleTweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
timerProgress.Start();
}
@@ -89,7 +147,6 @@ namespace TweetDick.Core{
public void HideNotification(){
browser.LoadHtml("","about:blank");
Location = new Point(-32000,-32000);
TopMost = false;
timerProgress.Stop();
}
@@ -105,12 +162,25 @@ namespace TweetDick.Core{
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
timerProgress.Stop();
timerProgress.Start();
UpdateTitle();
}
private void MoveToVisibleLocation(){
bool needsReactivating = Location.X == -32000;
UserConfig config = Program.UserConfig;
Screen screen = Screen.FromControl(owner);
if (config.DisplayNotificationTimer){
ClientSize = new Size(ClientSize.Width,122);
progressBarTimer.Visible = true;
}
else{
ClientSize = new Size(ClientSize.Width,118);
progressBarTimer.Visible = false;
}
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
screen = Screen.AllScreens[config.NotificationDisplay-1];
}
@@ -144,47 +214,13 @@ namespace TweetDick.Core{
break;
}
TopMost = true;
}
private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position))return;
timeLeft -= timerProgress.Interval;
progressBarTimer.SetValueInstant((int)Math.Min(1000,Math.Round(1050.0*(totalTime-timeLeft)/totalTime)));
if (timeLeft <= 0){
if (tweetQueue.Count > 0){
LoadNextNotification();
}
else if (autoHide){
HideNotification();
}
if (needsReactivating){
Program.SetWindowPos(Handle.ToInt32(),-1,Left,Top,Width,Height,0x0010); // HWND_TOPMOST, SWP_NOACTIVATE
}
}
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
if (e.CloseReason == CloseReason.UserClosing){
HideNotification();
tweetQueue.Clear();
e.Cancel = true;
}
}
private class MenuHandlerEmpty : IContextMenuHandler{
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear();
}
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
return false;
}
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){}
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
return false;
}
private void UpdateTitle(){
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
}
}
}

View File

@@ -0,0 +1,110 @@
using CefSharp;
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Handling{
abstract class ContextMenuBase : IContextMenuHandler{
private const int MenuOpenUrlInBrowser = 26500;
private const int MenuCopyUrl = 26501;
private const int MenuOpenImageInBrowser = 26502;
private const int MenuSaveImage = 26503;
private const int MenuCopyImageUrl = 26504;
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
RemoveSeparatorIfLast(model);
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#")){
model.AddItem((CefMenuCommand)MenuOpenUrlInBrowser,"Open in browser");
model.AddItem((CefMenuCommand)MenuCopyUrl,"Copy link address");
model.AddSeparator();
}
if (parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents){
model.AddItem((CefMenuCommand)MenuOpenImageInBrowser,"Open image in browser");
model.AddItem((CefMenuCommand)MenuSaveImage,"Save image as...");
model.AddItem((CefMenuCommand)MenuCopyImageUrl,"Copy image URL");
model.AddSeparator();
}
}
public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
switch((int)commandId){
case MenuOpenUrlInBrowser:
BrowserUtils.OpenExternalBrowser(parameters.LinkUrl);
break;
case MenuCopyUrl:
Clipboard.SetText(string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink,TextDataFormat.UnicodeText);
break;
case MenuOpenImageInBrowser:
BrowserUtils.OpenExternalBrowser(parameters.SourceUrl);
break;
case MenuSaveImage:
string fileName = GetImageFileName(parameters.SourceUrl);
string extension = Path.GetExtension(fileName);
string saveTarget;
using(SaveFileDialog dialog = new SaveFileDialog{
AutoUpgradeEnabled = true,
OverwritePrompt = true,
Title = "Save image",
FileName = fileName,
Filter = "Image ("+(string.IsNullOrEmpty(extension) ? "unknown" : extension)+")|*.*"
}){
saveTarget = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
if (saveTarget != null){
BrowserUtils.DownloadFileAsync(parameters.SourceUrl,saveTarget,ex => {
MessageBox.Show("An error occurred while downloading the image: "+ex.Message,Program.BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
});
}
break;
case MenuCopyImageUrl:
Clipboard.SetText(parameters.SourceUrl,TextDataFormat.UnicodeText);
break;
}
return false;
}
public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){}
public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
return false;
}
protected void RemoveSeparatorIfFirst(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);
}
}
protected void RemoveSeparatorIfLast(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);
}
}
private static string GetImageFileName(string url){
// twimg adds a colon after file extension
int dot = url.LastIndexOf('.');
if (dot != -1){
int colon = url.IndexOf(':',dot);
if (colon != -1){
url = url.Substring(0,colon);
}
}
// return file name
return BrowserUtils.GetFileNameFromUrl(url) ?? "unknown";
}
}
}

View File

@@ -0,0 +1,69 @@
using CefSharp;
namespace TweetDck.Core.Handling{
class ContextMenuBrowser : ContextMenuBase{
private const int MenuSettings = 26600;
private const int MenuAbout = 26601;
private const int MenuMute = 26602;
private readonly FormBrowser form;
public ContextMenuBrowser(FormBrowser form){
this.form = form;
}
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Remove(CefMenuCommand.Back);
model.Remove(CefMenuCommand.Forward);
model.Remove(CefMenuCommand.Print);
model.Remove(CefMenuCommand.ViewSource);
RemoveSeparatorIfFirst(model);
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
model.AddItem(CefMenuCommand.Reload,"Reload");
model.AddCheckItem((CefMenuCommand)MenuMute,"Mute Notifications");
model.SetChecked((CefMenuCommand)MenuMute,Program.UserConfig.MuteNotifications);
model.AddSeparator();
if (TweetNotification.IsReady){
model.AddItem((CefMenuCommand)MenuSettings,"Settings");
}
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
}
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
if (base.OnContextMenuCommand(browserControl,browser,frame,parameters,commandId,eventFlags)){
return true;
}
switch((int)commandId){
case MenuSettings:
form.InvokeSafe(() => {
form.OpenSettings();
});
return true;
case MenuAbout:
form.InvokeSafe(() => {
form.OpenAbout();
});
return true;
case MenuMute:
form.InvokeSafe(() => {
Program.UserConfig.MuteNotifications = !Program.UserConfig.MuteNotifications;
Program.UserConfig.Save();
});
return true;
}
return false;
}
}
}

View File

@@ -1,61 +0,0 @@
using CefSharp;
namespace TweetDick.Core.Handling{
class ContextMenuHandler : IContextMenuHandler{
private const int MenuSettings = 26500;
private const int MenuAbout = 26501;
private readonly FormBrowser form;
public ContextMenuHandler(FormBrowser form){
this.form = form;
}
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Remove(CefMenuCommand.Back);
model.Remove(CefMenuCommand.Forward);
model.Remove(CefMenuCommand.Print);
model.Remove(CefMenuCommand.ViewSource);
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);
}
model.AddItem(CefMenuCommand.Reload,"Reload");
model.AddSeparator();
if (TweetNotification.IsReady){
model.AddItem((CefMenuCommand)MenuSettings,"Settings");
model.AddSeparator();
}
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
}
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
switch((int)commandId){
case MenuSettings:
form.InvokeSafe(() => {
form.OpenSettings();
});
return true;
case MenuAbout:
form.InvokeSafe(() => {
form.OpenAbout();
});
return true;
}
return false;
}
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){}
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
return false;
}
}
}

View File

@@ -0,0 +1,12 @@
using CefSharp;
namespace TweetDck.Core.Handling{
class ContextMenuNotification : ContextMenuBase{
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear();
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
RemoveSeparatorIfLast(model);
}
}
}

View File

@@ -1,7 +1,9 @@
using System.Diagnostics;
using TweetDck.Core.Utils;
namespace TweetDick.Core.Handling{
namespace TweetDck.Core.Handling{
class TweetDeckBridge{
public static string LastRightClickedLink = string.Empty;
private readonly FormBrowser form;
public string BrandName{
@@ -10,6 +12,30 @@ namespace TweetDick.Core.Handling{
}
}
public string VersionTag{
get{
return Program.VersionTag;
}
}
public bool MuteNotifications{
get{
return Program.UserConfig.MuteNotifications;
}
}
public bool UpdateCheckEnabled{
get{
return Program.UserConfig.EnableUpdateCheck;
}
}
public string DismissedVersionTag{
get{
return Program.UserConfig.DismissedUpdate ?? string.Empty;
}
}
public TweetDeckBridge(FormBrowser form){
this.form = form;
}
@@ -26,6 +52,12 @@ namespace TweetDick.Core.Handling{
});
}
public void SetLastRightClickedLink(string link){
form.InvokeSafe(() => {
LastRightClickedLink = link;
});
}
public void OpenSettingsMenu(){
form.InvokeSafe(() => {
form.OpenSettings();
@@ -38,12 +70,31 @@ namespace TweetDick.Core.Handling{
});
}
public void OnTweetSound(){
form.InvokeSafe(() => {
form.OnTweetSound();
});
}
public void OnUpdateAccepted(string versionTag, string downloadUrl){
form.InvokeSafe(() => {
form.BeginUpdateProcess(versionTag,downloadUrl);
});
}
public void OnUpdateDismissed(string versionTag){
form.InvokeSafe(() => {
Program.UserConfig.DismissedUpdate = versionTag;
Program.UserConfig.Save();
});
}
public void OpenBrowser(string url){
Process.Start(url);
BrowserUtils.OpenExternalBrowser(url);
}
public void Log(string data){
Debug.WriteLine(data);
System.Diagnostics.Debug.WriteLine(data);
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Text;
namespace TweetDick.Core.Handling{
namespace TweetDck.Core.Handling{
sealed class TweetNotification{
private static string FontSizeClass { get; set; }
private static string HeadTag { get; set; }
@@ -17,10 +17,10 @@ namespace TweetDick.Core.Handling{
StringBuilder build = new StringBuilder();
build.Append(@"<article><div class='js-stream-item-content item-box js-show-detail'><div class='js-tweet tweet'>");
build.Append(@"<header class='tweet-header'>");
build.Append(@"<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></time>");
build.Append(@"<a target='_blank' rel='user' href='https://twitter.com/chylexMC' class='account-link link-complex block'>");
build.Append(@"<div class='obj-left item-img tweet-img'><img width='48' height='48' alt='chylexMC's avatar' src='https://pbs.twimg.com/profile_images/645532929930608642/J56NBJVY_normal.png' class='tweet-avatar avatar pull-right'></div>");
build.Append(@"<div class='nbfc'><span class='account-inline txt-ellipsis'><b class='fullname link-complex-target'>chylex</b> <span class='username txt-mute'>@chylexMC</span></span></div>");
build.Append(@"<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></time>");
build.Append(@"<a target='_blank' rel='user' href='https://twitter.com/chylexmc' class='account-link link-complex block'>");
build.Append(@"<div class='obj-left item-img tweet-img'><img width='48' height='48' alt='chylexmc's avatar' src='https://pbs.twimg.com/profile_images/645532929930608642/J56NBJVY_normal.png' class='tweet-avatar avatar pull-right'></div>");
build.Append(@"<div class='nbfc'><span class='account-inline txt-ellipsis'><b class='fullname link-complex-target'>chylex</b> <span class='username txt-mute'>@chylexmc</span></span></div>");
build.Append(@"</a>");
build.Append(@"</header>");
build.Append(@"<div class='tweet-body'><p class='js-tweet-text tweet-text with-linebreaks'>This is an example tweet, which lets you test the location and duration of popup notifications.</p></div>");
@@ -70,7 +70,7 @@ namespace TweetDick.Core.Handling{
public string GenerateHtml(){
StringBuilder build = new StringBuilder();
build.Append("<!DOCTYPE html>");
build.Append("<html class='os-windows ").Append(FontSizeClass).Append("'>");
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass).Append("'>");
build.Append("<head>").Append(HeadTag).Append("</head>");
build.Append("<body class='hearty'><div class='app-columns-container'><div class='column' style='width:100%'>");
build.Append(html);

View File

@@ -1,4 +1,6 @@
namespace TweetDick.Core.Other {
using TweetDck.Core.Controls;
namespace TweetDck.Core.Other {
sealed partial class FormAbout {
/// <summary>
/// Required designer variable.
@@ -23,7 +25,7 @@
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.labelAbout = new TweetDick.Core.Controls.RichTextLabel();
this.labelAbout = new TweetDck.Core.Controls.RichTextLabel();
this.SuspendLayout();
//
// labelAbout
@@ -41,6 +43,7 @@
this.labelAbout.TabIndex = 0;
this.labelAbout.TabStop = false;
this.labelAbout.Text = "";
this.labelAbout.Click += new System.EventHandler(this.labelAbout_Click);
//
// FormAbout
//

View File

@@ -1,20 +1,43 @@
using System.Text;
using System;
using System.Text;
using System.Windows.Forms;
using TweetDick.Core.Controls;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDick.Core.Other{
namespace TweetDck.Core.Other{
sealed partial class FormAbout : Form{
private const string GitHubLink = "https://github.com/chylex/TweetDuck";
public FormAbout(){
InitializeComponent();
Text = "About "+Program.BrandName;
Text = "About "+Program.BrandName+" "+Program.VersionTag;
StringBuilder build = new StringBuilder();
build.Append(@"\fs22").Append(Program.BrandName).Append(@" was created by chylex as a replacement to the discontinued TweetDeck client for Windows, and is released under the MIT license.\par ");
build.Append(@"Official Website: ").Append(RichTextLabel.AddLink(Program.Website)).Append(@"\line ");
build.Append(@"Source Code: ").Append(RichTextLabel.AddLink("https://github.com/chylex/TweetDick"));
build.Append(@"Source Code: ").Append(RichTextLabel.AddLink(GitHubLink));
labelAbout.Rtf = RichTextLabel.Wrap(build.ToString());
}
private void labelAbout_Click(object sender, EventArgs e){ // LinkClicked isn't working so fuck that
if (Cursor.Current != Cursors.Hand)return;
// I don't even give a fuck, someone else PR a proper fix please
int index = labelAbout.GetCharIndexFromPosition(((MouseEventArgs)e).Location);
if (IsClickingOn(index,Program.Website)){
BrowserUtils.OpenExternalBrowser(Program.Website);
}
else if (IsClickingOn(index,GitHubLink)){
BrowserUtils.OpenExternalBrowser(GitHubLink);
}
}
private bool IsClickingOn(int index, string substringSearch){
int substringIndex = labelAbout.Text.IndexOf(substringSearch,StringComparison.Ordinal);
return index >= substringIndex && index <= substringIndex+substringSearch.Length;
}
}
}

View File

@@ -1,6 +1,6 @@
using TweetDick.Core.Controls;
using TweetDck.Core.Controls;
namespace TweetDick.Core.Other {
namespace TweetDck.Core.Other {
partial class FormBackgroundWork {
/// <summary>
/// Required designer variable.

View File

@@ -1,8 +1,8 @@
using System;
using System.Windows.Forms;
using TweetDick.Core.Controls;
using TweetDck.Core.Controls;
namespace TweetDick.Core.Other{
namespace TweetDck.Core.Other{
partial class FormBackgroundWork : Form{
public FormBackgroundWork(){
InitializeComponent();

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Core.Other {
namespace TweetDck.Core.Other {
sealed partial class FormSettings {
/// <summary>
/// Required designer variable.
@@ -23,7 +23,6 @@
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormSettings));
this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
this.labelDisplay = new System.Windows.Forms.Label();
this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
@@ -34,16 +33,24 @@
this.radioLocBL = new System.Windows.Forms.RadioButton();
this.radioLocTR = new System.Windows.Forms.RadioButton();
this.radioLocTL = new System.Windows.Forms.RadioButton();
this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
this.tableColumn2Panel = new System.Windows.Forms.Panel();
this.groupUserInterface = new System.Windows.Forms.GroupBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayType = new System.Windows.Forms.Label();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
this.radioDurVeryLong = new System.Windows.Forms.RadioButton();
this.radioDurLong = new System.Windows.Forms.RadioButton();
this.radioDurMedium = new System.Windows.Forms.RadioButton();
this.radioDurShort = new System.Windows.Forms.RadioButton();
this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
this.groupNotificationLocation.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.groupNotificationDuration.SuspendLayout();
this.tableLayout.SuspendLayout();
this.tableColumn2Panel.SuspendLayout();
this.groupUserInterface.SuspendLayout();
this.groupNotificationDuration.SuspendLayout();
this.SuspendLayout();
//
// groupNotificationLocation
@@ -69,9 +76,9 @@
//
// labelDisplay
//
this.labelDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelDisplay.AutoSize = true;
this.labelDisplay.Location = new System.Drawing.Point(6, 154);
this.labelDisplay.Location = new System.Drawing.Point(6, 148);
this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDisplay.Name = "labelDisplay";
this.labelDisplay.Size = new System.Drawing.Size(41, 13);
this.labelDisplay.TabIndex = 8;
@@ -83,8 +90,7 @@
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDisplay.FormattingEnabled = true;
this.comboBoxDisplay.Location = new System.Drawing.Point(9, 170);
this.comboBoxDisplay.Margin = new System.Windows.Forms.Padding(3, 3, 3, 12);
this.comboBoxDisplay.Location = new System.Drawing.Point(9, 164);
this.comboBoxDisplay.Name = "comboBoxDisplay";
this.comboBoxDisplay.Size = new System.Drawing.Size(168, 21);
this.comboBoxDisplay.TabIndex = 7;
@@ -92,9 +98,9 @@
//
// labelEdgeDistance
//
this.labelEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelEdgeDistance.AutoSize = true;
this.labelEdgeDistance.Location = new System.Drawing.Point(6, 203);
this.labelEdgeDistance.Location = new System.Drawing.Point(6, 197);
this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelEdgeDistance.Name = "labelEdgeDistance";
this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13);
this.labelEdgeDistance.TabIndex = 6;
@@ -102,10 +108,10 @@
//
// trackBarEdgeDistance
//
this.trackBarEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
this.trackBarEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarEdgeDistance.LargeChange = 8;
this.trackBarEdgeDistance.Location = new System.Drawing.Point(6, 219);
this.trackBarEdgeDistance.Location = new System.Drawing.Point(6, 213);
this.trackBarEdgeDistance.Maximum = 40;
this.trackBarEdgeDistance.Minimum = 8;
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
@@ -181,6 +187,97 @@
this.radioLocTL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocTL.Click += new System.EventHandler(this.radioLoc_Click);
//
// tableLayout
//
this.tableLayout.ColumnCount = 2;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.Controls.Add(this.tableColumn2Panel, 1, 0);
this.tableLayout.Controls.Add(this.groupNotificationLocation, 0, 0);
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayout.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize;
this.tableLayout.Location = new System.Drawing.Point(0, 0);
this.tableLayout.Name = "tableLayout";
this.tableLayout.Padding = new System.Windows.Forms.Padding(3);
this.tableLayout.RowCount = 1;
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Size = new System.Drawing.Size(384, 282);
this.tableLayout.TabIndex = 2;
//
// tableColumn2Panel
//
this.tableColumn2Panel.Controls.Add(this.groupUserInterface);
this.tableColumn2Panel.Controls.Add(this.groupNotificationDuration);
this.tableColumn2Panel.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableColumn2Panel.Location = new System.Drawing.Point(192, 3);
this.tableColumn2Panel.Margin = new System.Windows.Forms.Padding(0);
this.tableColumn2Panel.Name = "tableColumn2Panel";
this.tableColumn2Panel.Size = new System.Drawing.Size(189, 276);
this.tableColumn2Panel.TabIndex = 3;
//
// groupUserInterface
//
this.groupUserInterface.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupUserInterface.Controls.Add(this.comboBoxTrayType);
this.groupUserInterface.Controls.Add(this.labelTrayType);
this.groupUserInterface.Controls.Add(this.checkUpdateNotifications);
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Location = new System.Drawing.Point(3, 128);
this.groupUserInterface.Name = "groupUserInterface";
this.groupUserInterface.Size = new System.Drawing.Size(183, 145);
this.groupUserInterface.TabIndex = 3;
this.groupUserInterface.TabStop = false;
this.groupUserInterface.Text = "User Interface";
//
// comboBoxTrayType
//
this.comboBoxTrayType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(9, 93);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(3, 3, 3, 12);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(168, 21);
this.comboBoxTrayType.TabIndex = 10;
this.comboBoxTrayType.SelectedIndexChanged += new System.EventHandler(this.comboBoxTrayType_SelectedIndexChanged);
//
// labelTrayType
//
this.labelTrayType.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.labelTrayType.AutoSize = true;
this.labelTrayType.Location = new System.Drawing.Point(6, 77);
this.labelTrayType.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelTrayType.Name = "labelTrayType";
this.labelTrayType.Size = new System.Drawing.Size(52, 13);
this.labelTrayType.TabIndex = 9;
this.labelTrayType.Text = "Tray Icon";
//
// checkUpdateNotifications
//
this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 45);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3);
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(115, 17);
this.checkUpdateNotifications.TabIndex = 5;
this.checkUpdateNotifications.Text = "Check for Updates";
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
this.checkUpdateNotifications.CheckedChanged += new System.EventHandler(this.checkUpdateNotifications_CheckedChanged);
//
// checkNotificationTimer
//
this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(6, 21);
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
this.checkNotificationTimer.Name = "checkNotificationTimer";
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
this.checkNotificationTimer.TabIndex = 4;
this.checkNotificationTimer.Text = "Display Notification Timer";
this.checkNotificationTimer.UseVisualStyleBackColor = true;
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
//
// groupNotificationDuration
//
this.groupNotificationDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
@@ -189,10 +286,10 @@
this.groupNotificationDuration.Controls.Add(this.radioDurLong);
this.groupNotificationDuration.Controls.Add(this.radioDurMedium);
this.groupNotificationDuration.Controls.Add(this.radioDurShort);
this.groupNotificationDuration.Location = new System.Drawing.Point(195, 6);
this.groupNotificationDuration.Location = new System.Drawing.Point(3, 3);
this.groupNotificationDuration.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 118);
this.groupNotificationDuration.TabIndex = 1;
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 119);
this.groupNotificationDuration.TabIndex = 2;
this.groupNotificationDuration.TabStop = false;
this.groupNotificationDuration.Text = "Notification Duration";
//
@@ -248,23 +345,6 @@
this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click);
//
// tableLayout
//
this.tableLayout.ColumnCount = 2;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.Controls.Add(this.groupNotificationLocation, 0, 0);
this.tableLayout.Controls.Add(this.groupNotificationDuration, 1, 0);
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayout.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize;
this.tableLayout.Location = new System.Drawing.Point(0, 0);
this.tableLayout.Name = "tableLayout";
this.tableLayout.Padding = new System.Windows.Forms.Padding(3);
this.tableLayout.RowCount = 1;
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Size = new System.Drawing.Size(384, 282);
this.tableLayout.TabIndex = 2;
//
// FormSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -272,7 +352,7 @@
this.ClientSize = new System.Drawing.Size(384, 282);
this.Controls.Add(this.tableLayout);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = TweetDick.Properties.Resources.icon;
this.Icon = ((System.Drawing.Icon)(TweetDck.Properties.Resources.ResourceManager.GetObject("icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormSettings";
@@ -281,9 +361,12 @@
this.groupNotificationLocation.ResumeLayout(false);
this.groupNotificationLocation.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.tableLayout.ResumeLayout(false);
this.tableColumn2Panel.ResumeLayout(false);
this.groupUserInterface.ResumeLayout(false);
this.groupUserInterface.PerformLayout();
this.groupNotificationDuration.ResumeLayout(false);
this.groupNotificationDuration.PerformLayout();
this.tableLayout.ResumeLayout(false);
this.ResumeLayout(false);
}
@@ -298,13 +381,19 @@
private System.Windows.Forms.RadioButton radioLocTL;
private System.Windows.Forms.Label labelEdgeDistance;
private System.Windows.Forms.TrackBar trackBarEdgeDistance;
private System.Windows.Forms.Label labelDisplay;
private System.Windows.Forms.ComboBox comboBoxDisplay;
private System.Windows.Forms.TableLayoutPanel tableLayout;
private System.Windows.Forms.Panel tableColumn2Panel;
private System.Windows.Forms.GroupBox groupUserInterface;
private System.Windows.Forms.GroupBox groupNotificationDuration;
private System.Windows.Forms.RadioButton radioDurVeryLong;
private System.Windows.Forms.RadioButton radioDurLong;
private System.Windows.Forms.RadioButton radioDurMedium;
private System.Windows.Forms.RadioButton radioDurShort;
private System.Windows.Forms.Label labelDisplay;
private System.Windows.Forms.ComboBox comboBoxDisplay;
private System.Windows.Forms.TableLayoutPanel tableLayout;
private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.CheckBox checkUpdateNotifications;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayType;
}
}

View File

@@ -1,9 +1,9 @@
using System;
using System.Windows.Forms;
using TweetDick.Configuration;
using TweetDick.Core.Handling;
using TweetDck.Configuration;
using TweetDck.Core.Handling;
namespace TweetDick.Core.Other{
namespace TweetDck.Core.Other{
sealed partial class FormSettings : Form{
private static UserConfig Config{
get{
@@ -53,7 +53,15 @@ namespace TweetDick.Core.Other{
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay);
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior,0),comboBoxTrayType.Items.Count-1);
trackBarEdgeDistance.Value = Config.NotificationEdgeDistance;
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
}
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
@@ -115,5 +123,24 @@ namespace TweetDick.Core.Other{
notification.ShowNotificationForSettings(true);
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
}
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
notification.ShowNotificationForSettings(true);
}
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
}
}
}

103
Core/Other/FormUpdateDownload.Designer.cs generated Normal file
View File

@@ -0,0 +1,103 @@
namespace TweetDck.Core.Other {
partial class FormUpdateDownload {
/// <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.progressDownload = new System.Windows.Forms.ProgressBar();
this.btnCancel = new System.Windows.Forms.Button();
this.labelDescription = new System.Windows.Forms.Label();
this.labelStatus = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// progressDownload
//
this.progressDownload.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressDownload.Location = new System.Drawing.Point(12, 32);
this.progressDownload.MarqueeAnimationSpeed = 40;
this.progressDownload.Maximum = 1000;
this.progressDownload.Name = "progressDownload";
this.progressDownload.Size = new System.Drawing.Size(361, 23);
this.progressDownload.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressDownload.TabIndex = 0;
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.AutoSize = true;
this.btnCancel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnCancel.Location = new System.Drawing.Point(317, 61);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCancel.Size = new System.Drawing.Size(56, 23);
this.btnCancel.TabIndex = 1;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// labelDescription
//
this.labelDescription.AutoSize = true;
this.labelDescription.Location = new System.Drawing.Point(9, 13);
this.labelDescription.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3);
this.labelDescription.Name = "labelDescription";
this.labelDescription.Size = new System.Drawing.Size(0, 13);
this.labelDescription.TabIndex = 2;
//
// labelStatus
//
this.labelStatus.AutoSize = true;
this.labelStatus.Location = new System.Drawing.Point(9, 62);
this.labelStatus.Name = "labelStatus";
this.labelStatus.Size = new System.Drawing.Size(0, 13);
this.labelStatus.TabIndex = 3;
//
// FormUpdateDownload
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(385, 96);
this.Controls.Add(this.labelStatus);
this.Controls.Add(this.labelDescription);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.progressDownload);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(TweetDck.Properties.Resources.ResourceManager.GetObject("icon")));
this.MaximizeBox = false;
this.Name = "FormUpdateDownload";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormUpdateDownload_FormClosing);
this.Shown += new System.EventHandler(this.FormUpdateDownload_Shown);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ProgressBar progressDownload;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.Label labelDescription;
private System.Windows.Forms.Label labelStatus;
}
}

View File

@@ -0,0 +1,108 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Other{
sealed partial class FormUpdateDownload : Form{
public string InstallerPath{
get{
return Path.Combine(Path.GetTempPath(),updateInfo.FileName);
}
}
public enum Status{
Waiting, Failed, Cancelled, Manual, Succeeded
}
public Status UpdateStatus { get; private set; }
private readonly WebClient webClient;
private readonly UpdateInfo updateInfo;
public FormUpdateDownload(UpdateInfo info){
InitializeComponent();
this.webClient = new WebClient{ Proxy = null };
this.webClient.Headers[HttpRequestHeader.UserAgent] = BrowserUtils.HeaderUserAgent;
this.updateInfo = info;
this.UpdateStatus = Status.Waiting;
Disposed += (sender, args) => webClient.Dispose();
webClient.DownloadProgressChanged += webClient_DownloadProgressChanged;
webClient.DownloadFileCompleted += webClient_DownloadFileCompleted;
Text = "Updating "+Program.BrandName;
labelDescription.Text = "Downloading version "+info.VersionTag+"...";
}
private void FormUpdateDownload_Shown(object sender, EventArgs e){
webClient.DownloadFileAsync(new Uri(updateInfo.DownloadUrl),InstallerPath);
}
private void btnCancel_Click(object sender, EventArgs e){
webClient.CancelAsync();
btnCancel.Enabled = false;
}
private void FormUpdateDownload_FormClosing(object sender, FormClosingEventArgs e){
if (UpdateStatus == Status.Waiting){
e.Cancel = true;
webClient.CancelAsync();
UpdateStatus = e.CloseReason == CloseReason.UserClosing ? Status.Cancelled : Status.Manual; // manual will exit the app
}
}
private void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e){
this.InvokeSafe(() => {
if (e.TotalBytesToReceive == -1){
if (progressDownload.Style != ProgressBarStyle.Marquee){
progressDownload.Style = ProgressBarStyle.Continuous;
progressDownload.SetValueInstant(1000);
}
labelStatus.Text = (e.BytesReceived/(1024.0*1024.0)).ToString("0.0")+" MB";
}
else{
if (progressDownload.Style != ProgressBarStyle.Continuous){
progressDownload.Style = ProgressBarStyle.Continuous;
}
progressDownload.SetValueInstant(e.ProgressPercentage*10);
labelStatus.Text = (e.BytesReceived/(1024.0*1024.0)).ToString("0.0")+" / "+(e.TotalBytesToReceive/(1024.0*1024.0)).ToString("0.0")+" MB";
}
});
}
private void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e){
this.InvokeSafe(() => {
if (e.Cancelled){
if (UpdateStatus == Status.Waiting){
UpdateStatus = Status.Cancelled;
}
}
else if (e.Error != null){
Program.Log(e.Error.ToString());
if (MessageBox.Show("Could not download the update: "+e.Error.Message+"\r\n\r\nDo you want to open the website and try downloading the update manually?","Update Has Failed",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1) == DialogResult.Yes){
BrowserUtils.OpenExternalBrowser(Program.Website);
UpdateStatus = Status.Manual;
}
else{
UpdateStatus = Status.Failed;
}
}
else{
UpdateStatus = Status.Succeeded;
}
Close();
});
}
}
}

89
Core/TrayIcon.Designer.cs generated Normal file
View File

@@ -0,0 +1,89 @@
namespace TweetDck.Core {
partial class TrayIcon {
/// <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.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
this.restoreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.muteNotificationsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenu.SuspendLayout();
//
// trayIcon
//
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = global::TweetDck.Properties.Resources.icon;
this.notifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(this.trayIcon_MouseClick);
//
// contextMenuTray
//
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.restoreToolStripMenuItem,
this.muteNotificationsToolStripMenuItem,
this.closeToolStripMenuItem});
this.contextMenu.Name = "contextMenuTray";
this.contextMenu.ShowCheckMargin = true;
this.contextMenu.ShowImageMargin = false;
this.contextMenu.ShowItemToolTips = false;
this.contextMenu.Size = new System.Drawing.Size(174, 92);
this.contextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuTray_Opening);
this.contextMenu.Opened += new System.EventHandler(this.contextMenuTray_Opened);
//
// restoreToolStripMenuItem
//
this.restoreToolStripMenuItem.Name = "restoreToolStripMenuItem";
this.restoreToolStripMenuItem.Size = new System.Drawing.Size(173, 22);
this.restoreToolStripMenuItem.Text = "Restore";
this.restoreToolStripMenuItem.Click += new System.EventHandler(this.restoreToolStripMenuItem_Click);
//
// muteNotificationsToolStripMenuItem
//
this.muteNotificationsToolStripMenuItem.CheckOnClick = true;
this.muteNotificationsToolStripMenuItem.Name = "muteNotificationsToolStripMenuItem";
this.muteNotificationsToolStripMenuItem.Size = new System.Drawing.Size(173, 22);
this.muteNotificationsToolStripMenuItem.Text = "Mute Notifications";
this.muteNotificationsToolStripMenuItem.CheckedChanged += new System.EventHandler(this.muteNotificationsToolStripMenuItem_CheckedChanged);
//
// closeToolStripMenuItem
//
this.closeToolStripMenuItem.Name = "closeToolStripMenuItem";
this.closeToolStripMenuItem.Size = new System.Drawing.Size(173, 22);
this.closeToolStripMenuItem.Text = "Close";
this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click);
//
// TrayIcon
//
this.contextMenu.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.NotifyIcon notifyIcon;
private System.Windows.Forms.ContextMenuStrip contextMenu;
private System.Windows.Forms.ToolStripMenuItem restoreToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem closeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem muteNotificationsToolStripMenuItem;
}
}

63
Core/TrayIcon.cs Normal file
View File

@@ -0,0 +1,63 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace TweetDck.Core{
partial class TrayIcon : Component{
public enum Behavior{ // keep order
Disabled, DisplayOnly, MinimizeToTray, CloseToTray
}
public event EventHandler ClickRestore;
public event EventHandler ClickClose;
public bool Visible{
get{
return notifyIcon.Visible;
}
set{
notifyIcon.Visible = value;
}
}
public TrayIcon(){
InitializeComponent();
notifyIcon.Text = Program.BrandName;
}
// event handlers
private void trayIcon_MouseClick(object sender, MouseEventArgs e){
if (e.Button == MouseButtons.Left){
restoreToolStripMenuItem_Click(sender,e);
}
}
private void contextMenuTray_Opening(object sender, CancelEventArgs e){
muteNotificationsToolStripMenuItem.CheckedChanged -= muteNotificationsToolStripMenuItem_CheckedChanged;
muteNotificationsToolStripMenuItem.Checked = Program.UserConfig.MuteNotifications;
}
private void contextMenuTray_Opened(object sender, EventArgs e){
muteNotificationsToolStripMenuItem.CheckedChanged += muteNotificationsToolStripMenuItem_CheckedChanged;
}
private void restoreToolStripMenuItem_Click(object sender, EventArgs e){
if (ClickRestore != null){
ClickRestore(this,e);
}
}
private void muteNotificationsToolStripMenuItem_CheckedChanged(object sender, EventArgs e){
Program.UserConfig.MuteNotifications = muteNotificationsToolStripMenuItem.Checked;
Program.UserConfig.Save();
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e){
if (ClickClose != null){
ClickClose(this,e);
}
}
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Windows.Forms;
namespace TweetDck.Core.Utils{
static class BrowserUtils{
public static string HeaderAcceptLanguage{
get{
string culture = CultureInfo.CurrentCulture.Name;
if (culture == "en"){
return "en-us,en";
}
else{
return culture.ToLowerInvariant()+",en;q=0.9";
}
}
}
public static string HeaderUserAgent{
get{
return Program.BrandName+" "+Application.ProductVersion;
}
}
public static void OpenExternalBrowser(string url){ // TODO implement mailto
Process.Start(url);
}
public static string GetFileNameFromUrl(string url){
string file = Path.GetFileName(new Uri(url).AbsolutePath);
return string.IsNullOrEmpty(file) ? null : file;
}
public static void DownloadFileAsync(string url, string target, Action<Exception> onFailure){
WebClient client = new WebClient{ Proxy = null };
client.Headers[HttpRequestHeader.UserAgent] = HeaderUserAgent;
client.DownloadFileCompleted += (sender, args) => {
if (args.Error != null){
onFailure(args.Error);
}
};
client.DownloadFileAsync(new Uri(url),target);
}
}
}

17
Core/Utils/UpdateInfo.cs Normal file
View File

@@ -0,0 +1,17 @@
namespace TweetDck.Core.Utils{
class UpdateInfo{
public readonly string VersionTag;
public readonly string DownloadUrl;
public string FileName{
get{
return BrowserUtils.GetFileNameFromUrl(DownloadUrl) ?? Program.BrandName+".Update.exe";
}
}
public UpdateInfo(string versionTag, string downloadUrl){
this.VersionTag = versionTag;
this.DownloadUrl = downloadUrl;
}
}
}

View File

View File

@@ -1,6 +1,6 @@
using TweetDick.Core.Controls;
using TweetDck.Core.Controls;
namespace TweetDick.Migration {
namespace TweetDck.Migration {
partial class FormMigrationQuestion {
/// <summary>
/// Required designer variable.

View File

@@ -1,9 +1,9 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using TweetDick.Core.Controls;
using TweetDck.Core.Controls;
namespace TweetDick.Migration{
namespace TweetDck.Migration{
partial class FormMigrationQuestion : Form{
public MigrationDecision Decision { get; private set; }

View File

@@ -1,8 +1,8 @@
using System;
using Shell32;
using System.IO;
using Shell32;
namespace TweetDick.Migration.Helpers{
namespace TweetDck.Migration.Helpers{
sealed class LnkEditor{
private readonly ShellLinkObject obj;

View File

@@ -2,7 +2,7 @@
using System.Diagnostics;
using System.Linq;
namespace TweetDick.Migration.Helpers{
namespace TweetDck.Migration.Helpers{
static class ProgramProcessSearch{
public static Process FindProcessWithWindowByName(string name){
try{

View File

@@ -2,7 +2,7 @@
using System.Linq;
using Microsoft.Win32;
namespace TweetDick.Migration.Helpers{
namespace TweetDck.Migration.Helpers{
static class ProgramRegistrySearch{
public static string FindByDisplayName(string displayName){
Predicate<RegistryKey> predicate = key => displayName.Equals(key.GetValue("DisplayName") as string,StringComparison.OrdinalIgnoreCase);

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Migration{
namespace TweetDck.Migration{
enum MigrationDecision{
/// <summary>
/// Copies the important files and then deletes the TweetDeck folder.

View File

@@ -1,15 +1,15 @@
using Microsoft.Win32;
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TweetDick.Core.Other;
using TweetDick.Migration.Helpers;
using Microsoft.Win32;
using TweetDck.Core.Other;
using TweetDck.Migration.Helpers;
namespace TweetDick.Migration{
namespace TweetDck.Migration{
static class MigrationManager{
private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"twitter");
private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent,"TweetDeck");

View File

@@ -1,17 +1,18 @@
using CefSharp;
using System;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using TweetDick.Configuration;
using TweetDick.Core;
using TweetDick.Migration;
using CefSharp;
using TweetDck.Configuration;
using TweetDck.Core;
using TweetDck.Migration;
using TweetDck.Core.Utils;
[assembly: CLSCompliant(true)]
namespace TweetDick{
namespace TweetDck{
static class Program{
#if DUCK
public const string BrandName = "TweetDuck";
@@ -21,21 +22,17 @@ namespace TweetDick{
public const string Website = "http://tweetdick.chylex.com";
#endif
public const string VersionTag = "1.2";
public const string VersionFull = "1.2.0.0";
public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName);
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
public static UserConfig UserConfig { get; private set; }
private static string HeaderAcceptLanguage{
public static string LogFile{
get{
string culture = CultureInfo.CurrentCulture.Name;
if (culture == "en"){
return "en-us,en";
}
else{
return culture.ToLowerInvariant()+",en;q=0.9";
}
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"td-log.txt");
}
}
@@ -45,6 +42,9 @@ namespace TweetDick{
[DllImport("Shell32.dll")]
public static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
[STAThread]
private static void Main(){
Application.EnableVisualStyles();
@@ -72,8 +72,8 @@ namespace TweetDick{
};
Cef.Initialize(new CefSettings{
AcceptLanguageList = HeaderAcceptLanguage,
UserAgent = BrandName+" "+Application.ProductVersion,
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
UserAgent = BrowserUtils.HeaderUserAgent,
Locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
CachePath = StoragePath,
#if !DEBUG
@@ -95,29 +95,37 @@ namespace TweetDick{
Cef.Shutdown();
};
Application.Run(new FormBrowser());
FormBrowser mainForm = new FormBrowser();
Application.Run(mainForm);
if (mainForm.UpdateInstallerPath != null){
Cef.Shutdown();
Process.Start(mainForm.UpdateInstallerPath,"/SP- /SILENT /NOICONS /CLOSEAPPLICATIONS");
Application.Exit();
}
}
public static void HandleException(string message, Exception e){
Log(e.ToString());
if (MessageBox.Show(message+"\r\nDo you want to open the log file to report the issue?",BrandName+" Has Failed :(",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
Process.Start(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"td-log.txt"));
Process.Start(LogFile);
}
}
public static void Log(string data){
StringBuilder build = new StringBuilder();
if (!File.Exists("td-log.txt")){
build.Append("Please, report all issues to: https://github.com/chylex/TweetDick/issues\r\n\r\n");
if (!File.Exists(LogFile)){
build.Append("Please, report all issues to: https://github.com/chylex/TweetDuck/issues\r\n\r\n");
}
build.Append("[").Append(DateTime.Now.ToString("G")).Append("]\r\n");
build.Append(data).Append("\r\n\r\n");
try{
File.AppendAllText("td-log.txt",build.ToString(),Encoding.UTF8);
File.AppendAllText(LogFile,build.ToString(),Encoding.UTF8);
}catch{
// oops
}

View File

@@ -1,7 +1,7 @@
using System.Reflection;
using System.Runtime.InteropServices;
using TweetDick;
using System.Resources;
using TweetDck;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -33,7 +33,7 @@ using System.Resources;
// 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")]
[assembly: AssemblyVersion(Program.VersionFull)]
[assembly: AssemblyFileVersion(Program.VersionFull)]
[assembly: NeutralResourcesLanguageAttribute("en")]

View File

@@ -8,7 +8,7 @@
// </auto-generated>
//------------------------------------------------------------------------------
namespace TweetDick.Properties {
namespace TweetDck.Properties {
using System;
@@ -39,7 +39,7 @@ namespace TweetDick.Properties {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TweetDick.Properties.Resources", typeof(Resources).Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TweetDck.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;

13
README.md Normal file
View File

@@ -0,0 +1,13 @@
# Build Instructions
The program was build using Visual Studio 2013. After opening the solution, make sure you have **CefSharp.WinForms** and **Microsoft.VC120.CRT.JetBrains** included - if not, download them using NuGet. For **CefSharp**, you will need version 49 or newer currently available as a pre-release.
```
PM> Install-Package CefSharp.WinForms -Pre
PM> Install-Package Microsoft.VC120.CRT.JetBrains
```
TweetD\*ck comes in two variants - TweetDick and TweetDuck. The solution includes both configurations under the names **Release Dick** and **Release Duck**, so make sure to select the correct one, or build both using Batch Build.
After building, run **_postbuild.bat** which deletes unnecessary files that CefSharp adds after post-build events >_>
Built files are then available in **bin/x86** and/or **bin/x64**.

View File

@@ -1,17 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Linq;
namespace TweetDick.Resources{
namespace TweetDck.Resources{
static class ScriptLoader{
public static string LoadResource(string name){
try{
return File.ReadAllText(name,Encoding.UTF8);
return File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,name),Encoding.UTF8);
}catch(Exception ex){
MessageBox.Show("Unfortunately, "+Program.BrandName+" could not load the "+name+" file. The program will continue running with limited functionality.\r\n\r\n"+ex.Message,Program.BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
return null;
}
}
public static IList<string> LoadResources(params string[] names){
return names.Select(LoadResource).ToList();
}
}
}

View File

@@ -1,38 +1,28 @@
(function($,$TD){
(function($,$TD,TD){
//
// Constant: List of valid font size classes.
//
var fontSizeClasses = [ "txt-base-smallest", "txt-base-small", "txt-base-medium", "txt-base-large", "txt-base-largest" ];
//
// Variable: Says whether TweetDick events was initialized.
// Variable: Says whether TweetD*ck events was initialized.
//
var isInitialized = false;
//
// Variable: Previous font size class in the <html> tag.
// Function: Initializes TweetD*ck events. Called after the website app is loaded.
//
var prevFontSizeClass;
//
// Function: Initializes TweetDick events. Called after the website app is loaded.
//
var initializeTweetDick = function(){
var initializeTweetDck = function(){
// Settings button hook
$("[data-action='settings-menu']").click(function(){
setTimeout(function(){
var menu = $(".js-dropdown-content").children("ul").first();
if (menu.length === 0)return;
menu.children(".drp-h-divider").last().after('<li class="is-selectable" data-std><a href="#" data-action>'+$TD.brandName+'</a></li><li class="drp-h-divider"></li>');
menu.children(".drp-h-divider").last().after('<li class="is-selectable" data-std><a href="#" data-action>'+$TD.brandName+' settings</a></li><li class="drp-h-divider"></li>');
var tweetDickBtn = menu.children("[data-std]").first();
var tweetDckBtn = menu.children("[data-std]").first();
tweetDickBtn.on("click","a",function(){
tweetDckBtn.on("click","a",function(){
$TD.openSettingsMenu();
});
tweetDickBtn.hover(function(){
tweetDckBtn.hover(function(){
$(this).addClass("is-selected");
},function(){
$(this).removeClass("is-selected");
@@ -40,105 +30,68 @@
},0);
});
// Tweet notifications
(function(){
var columns = $(".js-app-columns").first();
var refreshColumnObservers = function(){
columns.children().each(function(){
registerTweetObserverForColumn($(this));
});
};
new MutationObserver(refreshColumnObservers).observe(columns[0],{
childList: true
});
refreshColumnObservers();
})();
// Popup notifications
window.TD.controller.notifications.hasNotifications = function(){
return true;
};
window.TD.controller.notifications.isPermissionGranted = function(){
return true;
};
// Notification handling
$.subscribe("/notifications/new",function(obj){
for(var item of obj.items){
onNewTweet(obj.column,item);
}
});
// Finish init
$TD.loadFontSizeClass(TD.settings.getFontSize());
$TD.loadNotificationHeadContents(getNotificationHeadContents());
isInitialized = true;
};
//
// Function: Registers an observer to a TweetDeck column, which reports new tweets.
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
//
var registerTweetObserverForColumn = function(column){
if (column[0].hasAttribute("data-std-observed"))return;
var prependToFunction = function(func, extension){
return function(){
return extension.apply(this,arguments) === true ? undefined : func.apply(this,arguments);
};
};
var mid = column;
mid = mid.children().first();
mid = mid.children().first();
mid = mid.children(".js-column-content").first();
mid = mid.children(".js-column-scroller").first();
var container = mid.children(".js-chirp-container").first();
if (container.length === 0)return;
var scroller = container.parent();
new MutationObserver(function(mutations){
if (!container[0].hasAttribute("data-std-loaded")){
container[0].setAttribute("data-std-loaded","");
return;
}
var data = TD.controller.columnManager.get(column.attr("data-column"));
if (!data.model.getHasNotification())return;
if (scroller.scrollTop() != 0)return;
Array.prototype.forEach.call(mutations,function(mutation){
Array.prototype.forEach.call(mutation.addedNodes,function(node){
if (node.tagName !== "ARTICLE")return;
onNewTweet(column,node);
});
});
}).observe(container[0],{
childList: true
});
column[0].setAttribute("data-std-observed","");
//
// Function: Appends code at the end of a function.
//
var appendToFunction = function(func, extension){
return function(){
var res = func.apply(this,arguments);
extension.apply(this,arguments);
return res;
};
};
//
// Function: Event callback for a new tweet.
//
var onNewTweet = function(column, tweet){
var html = $(tweet.outerHTML);
var body = html.find(".tweet-body").first();
if (column.model.getHasNotification()){
var html = $(tweet.render({
withFooter: false,
withTweetActions: false,
withMediaPreview: false
}));
body.children("div.js-quote-detail").each(function(){
$(this).html("(quoted tweet)");
});
html.css("border","0");
body.children().not("p,div.js-quote-detail").remove();
var body = html.find(".tweet-body").first();
$TD.onTweetPopup(html.html(),html.find(".js-tweet-text:first").text().length); // TODO column & remove pic links from text()
};
body.children("div.js-quote-detail").each(function(){
$(this).html("(quoted tweet)");
$(this).removeClass("padding-al");
$(this).css("padding","6px");
});
//
// Function: Retrieves the font size using <html> class attribute.
//
var getFontSizeClass = function(){
for(var index = 0; index < fontSizeClasses.length; index++){
if (document.documentElement.classList.contains(fontSizeClasses[index])){
return fontSizeClasses[index];
}
body.children("footer").remove();
$TD.onTweetPopup(html.html(),tweet.fullLength); // TODO column
}
else if (column.model.getHasSound()){
$TD.onTweetSound(); // TODO disable original
}
return fontSizeClasses[0];
};
//
@@ -155,7 +108,7 @@
};
//
// Block: Observe the app <div> element and initialize TweetDick whenever possible.
// Block: Observe the app <div> element and initialize TweetD*ck whenever possible.
//
var app = $("body").children(".js-app");
@@ -164,7 +117,7 @@
isInitialized = false;
}
else if (!isInitialized && !app.hasClass("is-hidden")){
initializeTweetDick();
initializeTweetDck();
}
}).observe(app[0],{
attributes: true,
@@ -172,31 +125,28 @@
});
//
// Block: Observe changes in <html> class to update font size.
// Block: Hook into settings object to detect when the settings change.
//
new MutationObserver(function(mutations){
var fsClass = getFontSizeClass();
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize,function(name){
$TD.loadFontSizeClass(name);
});
if (fsClass != prevFontSizeClass){
prevFontSizeClass = fsClass;
$TD.loadFontSizeClass(fsClass);
}
}).observe(document.documentElement,{
attributes: true,
attributeFilter: [ "class" ]
TD.settings.setTheme = appendToFunction(TD.settings.setTheme,function(){
setTimeout(function(){
$TD.loadNotificationHeadContents(getNotificationHeadContents());
},0);
});
//
// Block: Observe stylesheet swapping.
// Block: Force popup notification settings
//
new MutationObserver(function(mutations){
$TD.loadNotificationHeadContents(getNotificationHeadContents());
}).observe(document.head.querySelector("[http-equiv='Default-Style']"),{
attributes: true,
attributeFilter: [ "content" ]
});
TD.controller.notifications.hasNotifications = function(){
return true;
};
$TD.loadNotificationHeadContents(getNotificationHeadContents());
TD.controller.notifications.isPermissionGranted = function(){
return true;
};
//
// Block: Hook into links to bypass default open function
@@ -215,7 +165,7 @@
var me = $(this);
var rel = me.attr("rel");
if (!me.is(".link-complex") && !(rel === "mediaPreview" && me.closest("#open-modal").length === 0) && rel !== "list"){
if (!me.is(".link-complex") && !(rel === "mediaPreview" && me.closest("#open-modal").length === 0) && rel !== "list" && rel !== "user"){
$TD.openBrowser(me.attr("href"));
}
@@ -230,4 +180,85 @@
onUrlOpened();
};
})();
})($,$TD);
TD.util.maybeOpenClickExternally = prependToFunction(TD.util.maybeOpenClickExternally,function(e){
if (e.ctrlKey){
$TD.openBrowser(e.currentTarget.getAttribute("href"));
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return true;
}
});
//
// Block: Expand shortened links on hover.
//
(function(){
var cutStart = function(str, search){
return _.startsWith(str,search) ? str.substr(search.length) : str;
};
$(document.body).delegate("a[data-full-url]","mouseenter mouseleave",function(e){
var me = $(this);
if (e.type === "mouseenter"){
var text = me.text();
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
return;
}
var expanded = me.attr("data-full-url");
expanded = cutStart(expanded,"https://");
expanded = cutStart(expanded,"http://");
expanded = cutStart(expanded,"www.");
me.css("word-break","break-all");
me.attr("td-prev-text",text);
me.text(expanded);
}
else if (e.type === "mouseleave"){
var prevText = me.attr("td-prev-text");
if (prevText){
me.text(prevText);
}
}
});
})();
//
// Block: Allow bypassing of t.co in context menus.
//
$(document.body).delegate("a","contextmenu",function(){
$TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
});
//
// Block: Hook into the notification sound effect.
//
(function(){
var soundEle = document.getElementById("update-sound");
soundEle.play = prependToFunction(soundEle.play,function(){
return $TD.muteNotifications;
});
})();
/* TODO document.getElementById("update-sound").play = function(){
$TD.onTweetSound();
};*/
//
// Block: Hook into mp4 video element clicking
//
$(document.body).delegate("video.js-media-gif","click",function(e){
var src = $(this).attr("src");
if (src.endsWith(".mp4")){
$TD.openBrowser(src);
e.preventDefault();
}
});
})($,$TD,TD);

View File

@@ -1,11 +1,35 @@
(function($TD){
//
// Block: Hook into links to bypass default open function
// Function: Bubbles up the parents until it hits an element with the specified tag (includes the first element), and returns true if the search was successful.
//
var bubbleParents = function(element, tag, callback){
do{
if (element.tagName == "A"){
callback(element);
return true;
}
}while((element = element.parentElement) != null);
return false;
};
//
// Block: Hook into links to bypass default open function.
//
document.body.addEventListener("click",function(e){
if (e.target.tagName == "A"){
$TD.openBrowser(e.target.getAttribute("href"));
if (bubbleParents(e.target,"A",function(ele){
$TD.openBrowser(ele.getAttribute("href"));
})){
e.preventDefault();
}
});
//
// Block: Allow bypassing of t.co in context menus.
//
document.body.addEventListener("contextmenu",function(e){
bubbleParents(e.target,"A",function(ele){
$TD.setLastRightClickedLink(element.getAttribute("data-full-url") || "");
});
});
})($TD);

121
Resources/update.js Normal file
View File

@@ -0,0 +1,121 @@
(function($,$TD){
//
// Variable: Current timeout ID for update checking.
//
var updateCheckTimeoutID;
//
// Function: Creates the update notification element. Removes the old one if already exists.
//
var createUpdateNotificationElement = function(version, download){
var ele = $("#tweetdck-update");
var existed = ele.length > 0;
if (existed > 0){
ele.remove();
}
var html = [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>"+$TD.brandName+" 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</button>",
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</button>",
"</div>",
"</div>"
];
$(document.body).append(html.join(""));
ele = $("#tweetdck-update");
var buttonDiv = ele.children("div.tdu-buttons").first();
ele.css({
color: "#fff",
backgroundColor: "rgb(32,94,138)",
position: "absolute",
left: "4px",
bottom: "4px",
width: "192px",
height: "86px",
display: existed ? "block" : "none",
borderRadius: "2px",
zIndex: 9999
});
ele.children("p.tdu-title").first().css({
fontSize: "17px",
fontWeight: "bold",
textAlign: "center",
letterSpacing: "0.2px",
margin: "4px auto 2px"
});
ele.children("p.tdu-info").first().css({
fontSize: "12px",
textAlign: "center",
margin: "2px auto 6px"
});
buttonDiv.css({
textAlign: "center"
});
buttonDiv.children().css({
margin: "0 4px",
minHeight: "25px",
boxShadow: "1px 1px 1px rgba(17,17,17,0.5)"
});
buttonDiv.find("span").css({
verticalAlign: "baseline"
});
ele.find("span.tdu-data-tag").first().css({
cursor: "pointer",
textDecoration: "underline"
});
buttonDiv.children(".tdu-btn-download").click(function(){
ele.remove();
$TD.onUpdateAccepted(version,download);
});
buttonDiv.children(".tdu-btn-dismiss").click(function(){
$TD.onUpdateDismissed(version);
ele.slideUp(function(){ ele.remove(); });
});
if (!existed){
ele.slideDown();
}
return ele;
};
//
// Function: Runs an update check and updates all DOM elements appropriately
//
var runUpdateCheck = function(){
clearTimeout(updateCheckTimeoutID);
updateCheckTimeoutID = setTimeout(runUpdateCheck,1000*60*60); // 1 hour
if (!$TD.updateCheckEnabled)return;
$.getJSON("https://api.github.com/repos/chylex/"+$TD.brandName+"/releases/latest",function(response){
var tagName = response.tag_name;
if (tagName != $TD.versionTag && tagName != $TD.dismissedVersionTag && response.assets.length > 0){
createUpdateNotificationElement(tagName,response.assets[0].browser_download_url);
}
});
};
//
// Block: Setup global functions.
//
window.TDGF_runUpdateCheck = runUpdateCheck;
runUpdateCheck();
})($,$TD);

View File

@@ -9,7 +9,7 @@
<ProjectGuid>{2389A7CD-E0D3-4706-8294-092929A33A2D}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TweetDick</RootNamespace>
<RootNamespace>TweetDck</RootNamespace>
<AssemblyName Condition=" '$(Configuration)' == 'Debug' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Dick' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Duck' ">TweetDuck</AssemblyName>
@@ -77,13 +77,15 @@
<ItemGroup>
<Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\UserConfig.cs" />
<Compile Include="Core\Controls\ControlExtensions.cs" />
<Compile Include="Core\Controls\FlatProgressBar.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Core\Controls\FlatProgressBar.Designer.cs">
<DependentUpon>FlatProgressBar.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\ContextMenuHandler.cs" />
<Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" />
<Compile Include="Core\FormBrowser.cs">
<SubType>Form</SubType>
</Compile>
@@ -96,6 +98,7 @@
<Compile Include="Core\FormNotification.Designer.cs">
<DependentUpon>FormNotification.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\ContextMenuNotification.cs" />
<Compile Include="Core\Handling\TweetNotification.cs" />
<Compile Include="Core\Controls\RichTextLabel.cs">
<SubType>Component</SubType>
@@ -122,6 +125,20 @@
<Compile Include="Core\Other\FormSettings.Designer.cs">
<DependentUpon>FormSettings.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormUpdateDownload.Designer.cs">
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
</Compile>
<Compile Include="Core\TrayIcon.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Core\TrayIcon.Designer.cs">
<DependentUpon>TrayIcon.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\UpdateInfo.cs" />
<Compile Include="Migration\FormMigrationQuestion.cs">
<SubType>Form</SubType>
</Compile>
@@ -201,6 +218,12 @@
<TargetPath>notification.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\update.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>update.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
@@ -217,10 +240,12 @@
<Import Project="packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.49.0.0-pre02\build\CefSharp.Common.targets')" />
<PropertyGroup>
<PostBuildEvent>del "$(TargetPath).config"
xcopy "$(ProjectDir)LICENSE" "$(TargetDir)" /Y
xcopy "$(ProjectDir)LICENSE.md" "$(TargetDir)" /Y
del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y</PostBuildEvent>
ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y</PostBuildEvent>
</PropertyGroup>
<!-- 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.

View File

@@ -3,7 +3,7 @@ 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}") = "TweetDick", "TweetDick.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TweetDck", "TweetDck.csproj", "{2389A7CD-E0D3-4706-8294-092929A33A2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution