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

Compare commits

...

54 Commits
0.9.1 ... 1.1

Author SHA1 Message Date
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
7de79786c9 Disable start menu icons (handled by installer) and delete shortcuts that cannot be moved 2016-04-14 18:11:43 +02:00
d3d1fc808e Update assembly description 2016-04-14 18:09:20 +02:00
1ab9e35c6e Update version to 1.0 2016-04-14 17:27:08 +02:00
e6e4d0ba75 Change migration link generator to not delete existing links 2016-04-14 14:10:25 +02:00
147c268ef8 Add exception handling and logging 2016-04-14 13:13:38 +02:00
ebd17e1544 Work on migration (registry cleanup, refactoring) 2016-04-14 13:13:18 +02:00
039233c782 Fix browser opening when clicking on list urls, and change == to === 2016-04-14 12:37:42 +02:00
57484c8bf7 Add uninstall prompt since public beta did not uninstall TweetDeck correctly 2016-04-14 12:36:35 +02:00
74fec18146 Cleanup resources from the uninstaller process 2016-04-14 02:41:44 +02:00
00341e984c Fix TweetDeck uninstaller 2016-04-14 02:40:58 +02:00
c172e7aa3d Protect lnk replacement if the target filename already exists 2016-04-13 20:55:54 +02:00
1030555bb5 Add start menu lnk migration 2016-04-13 15:52:23 +02:00
c53636c013 Disable moving the notification window if not in Settings menu & having Custom pos enabled 2016-04-13 11:22:45 +02:00
cf450447e5 Fix support for old config files 2016-04-13 11:11:53 +02:00
81c29ab50c Fix some issues with multi-monitor setups (change default loc, fix config saving before window loaded) 2016-04-13 02:33:21 +02:00
f99435ff6a Add a null check to FlatProgressBar.brush 2016-04-13 02:24:24 +02:00
39f554693d Address static code analysis issues (disposing objects, neutral language and CLSCompliant attribute) 2016-04-12 23:33:37 +02:00
38539c4a9b Add a post-build batch file to delete unwanted files from the release folders 2016-04-12 18:07:37 +02:00
44 changed files with 1216 additions and 243 deletions

View File

@@ -3,7 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
namespace TweetDick.Configuration{ namespace TweetDck.Configuration{
class LockManager{ class LockManager{
private readonly string file; private readonly string file;
private FileStream lockStream; private FileStream lockStream;

View File

@@ -3,36 +3,47 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Binary;
using TweetDick.Core.Handling; using TweetDck.Core.Handling;
namespace TweetDick.Configuration{ namespace TweetDck.Configuration{
[Serializable] [Serializable]
sealed class UserConfig{ 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 // START OF CONFIGURATION
public bool IgnoreMigration { get; set; } public bool IgnoreMigration { get; set; }
public bool IgnoreUninstallCheck { get; set; }
public bool IsMaximized { get; set; } public bool IsMaximized { get; set; }
public Point WindowLocation { get; set; } public Point WindowLocation { get; set; }
public Size WindowSize { get; set; } public Size WindowSize { get; set; }
public bool MinimizeToTray { get; set; }
public bool DisplayNotificationTimer { get; set; }
public TweetNotification.Duration NotificationDuration { get; set; } public TweetNotification.Duration NotificationDuration { get; set; }
public TweetNotification.Position NotificationPosition { get; set; } public TweetNotification.Position NotificationPosition { get; set; }
public Point CustomNotificationPosition { get; set; } public Point CustomNotificationPosition { get; set; }
public int NotificationEdgeDistance { get; set; } public int NotificationEdgeDistance { get; set; }
public int NotificationDisplay { get; set; } public int NotificationDisplay { get; set; }
public bool EnableUpdateCheck { get; set; }
public string DismissedUpdate { get; set; }
public bool IsCustomWindowLocationSet{ public bool IsCustomWindowLocationSet{
get{ get{
return WindowLocation.X != 32000; return WindowLocation.X != -32000 && WindowLocation.X != 32000;
} }
} }
public bool IsCustomNotificationPositionSet{ public bool IsCustomNotificationPositionSet{
get{ get{
return CustomNotificationPosition.X != 32000; return CustomNotificationPosition.X != -32000 && CustomNotificationPosition.X != 32000;
} }
} }
@@ -41,15 +52,36 @@ namespace TweetDick.Configuration{
[NonSerialized] [NonSerialized]
private string file; private string file;
private int fileVersion;
private UserConfig(string file){ private UserConfig(string file){
this.file = file; this.file = file;
IsMaximized = true; IsMaximized = true;
WindowLocation = new Point(32000,32000); DisplayNotificationTimer = true;
WindowLocation = new Point(-32000,-32000);
NotificationDuration = TweetNotification.Duration.Medium; NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight; NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = new Point(32000,32000); CustomNotificationPosition = new Point(-32000,-32000);
NotificationEdgeDistance = 8; 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(){ public bool Save(){
@@ -70,8 +102,8 @@ namespace TweetDick.Configuration{
} }
return true; return true;
}catch(Exception){ }catch(Exception e){
// TODO Program.HandleException("Could not save the configuration file.",e);
return false; return false;
} }
} }
@@ -86,11 +118,15 @@ namespace TweetDick.Configuration{
config.file = file; config.file = file;
} }
} }
if (config != null){
config.UpgradeFile();
}
break; break;
}catch(FileNotFoundException){ }catch(FileNotFoundException){
}catch(Exception){ }catch(Exception e){
// TODO Program.HandleException("Could not open the configuration file.",e);
} }
} }
@@ -100,5 +136,18 @@ namespace TweetDick.Configuration{
private static string GetBackupFile(string file){ private static string GetBackupFile(string file){
return file+".bak"; 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="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.Common" version="49.0.0-pre02" targetFramework="net40-Client" />
<package id="CefSharp.WinForms" 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> </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 { partial class FlatProgressBar {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
@@ -10,6 +10,8 @@
/// </summary> /// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) { protected override void Dispose(bool disposing) {
if (brush != null)brush.Dispose();
if (disposing && (components != null)) { if (disposing && (components != null)) {
components.Dispose(); components.Dispose();
} }

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDick.Core.Controls{ namespace TweetDck.Core.Controls{
public partial class RichTextLabel : RichTextBox{ public partial class RichTextLabel : RichTextBox{
/// <summary> /// <summary>
/// Wraps the body of a RTF formatted string with default tags and formatting. /// 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 { sealed partial class FormBrowser {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
@@ -23,23 +23,33 @@
/// the contents of this method with the code editor. /// the contents of this method with the code editor.
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormBrowser)); this.components = new System.ComponentModel.Container();
this.trayIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.SuspendLayout(); this.SuspendLayout();
// //
// trayIcon
//
this.trayIcon.Icon = global::TweetDck.Properties.Resources.icon;
this.trayIcon.Click += new System.EventHandler(this.trayIcon_Click);
//
// FormBrowser // FormBrowser
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Icon = TweetDick.Properties.Resources.icon; this.ClientSize = new System.Drawing.Size(284, 262);
this.Location = new System.Drawing.Point(32000, 32000); this.Icon = global::TweetDck.Properties.Resources.icon;
this.Location = new System.Drawing.Point(-32000, -32000);
this.Name = "FormBrowser"; this.Name = "FormBrowser";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd); this.ResizeEnd += new System.EventHandler(this.FormBrowser_ResizeEnd);
this.Resize += new System.EventHandler(this.FormBrowser_Resize);
this.ResumeLayout(false); this.ResumeLayout(false);
} }
#endregion #endregion
private System.Windows.Forms.NotifyIcon trayIcon;
} }
} }

View File

@@ -1,15 +1,16 @@
using System.Windows.Forms; using System;
using CefSharp.WinForms;
using System;
using System.Linq; using System.Linq;
using TweetDick.Configuration; using System.Windows.Forms;
using CefSharp; using CefSharp;
using TweetDick.Core.Handling; using CefSharp.WinForms;
using TweetDick.Core.Other; using TweetDck.Configuration;
using System.Drawing; using TweetDck.Core.Handling;
using TweetDick.Resources; using TweetDck.Core.Other;
using TweetDck.Resources;
using TweetDck.Core.Utils;
using TweetDck.Core.Controls;
namespace TweetDick.Core{ namespace TweetDck.Core{
sealed partial class FormBrowser : Form{ sealed partial class FormBrowser : Form{
private static UserConfig Config{ private static UserConfig Config{
get{ get{
@@ -17,12 +18,17 @@ namespace TweetDick.Core{
} }
} }
public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
private readonly TweetDeckBridge bridge; private readonly TweetDeckBridge bridge;
private readonly FormNotification notification; private readonly FormNotification notification;
private FormSettings currentFormSettings; private FormSettings currentFormSettings;
private FormAbout currentFormAbout; private FormAbout currentFormAbout;
private bool isLoaded;
private FormWindowState prevState;
public FormBrowser(){ public FormBrowser(){
InitializeComponent(); InitializeComponent();
@@ -31,29 +37,24 @@ namespace TweetDick.Core{
bridge = new TweetDeckBridge(this); 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.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd; browser.FrameLoadEnd += Browser_FrameLoadEnd;
browser.RegisterJsObject("$TD",bridge); browser.RegisterJsObject("$TD",bridge);
Controls.Add(browser); Controls.Add(browser);
notification = new FormNotification(this,bridge,true); Disposed += (sender, args) => browser.Dispose();
notification = new FormNotification(this,bridge,true){ CanMoveWindow = () => false };
notification.Show(); notification.Show();
}
protected override void WndProc(ref Message m){ trayIcon.Text = Program.BrandName;
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){ private void ShowChildForm(Form form){
form.Show(this); 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 // window setup
@@ -70,6 +71,9 @@ namespace TweetDick.Core{
Size = Screen.PrimaryScreen.WorkingArea.Size; Size = Screen.PrimaryScreen.WorkingArea.Size;
WindowState = FormWindowState.Maximized; WindowState = FormWindowState.Maximized;
} }
prevState = WindowState;
isLoaded = true;
} }
// active event handlers // active event handlers
@@ -91,26 +95,47 @@ namespace TweetDick.Core{
} }
} }
private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves private void FormBrowser_Resize(object sender, EventArgs e){
Config.WindowLocation = Location; if (!isLoaded)return;
Config.WindowSize = Size;
Config.Save(); if (WindowState != prevState){
prevState = WindowState;
if (WindowState == FormWindowState.Minimized){
if (Config.MinimizeToTray){
Hide(); // hides taskbar too?! welp that works I guess
trayIcon.Visible = true;
}
}
else{
FormBrowser_ResizeEnd(sender,e);
}
}
} }
private void FormBrowser_WindowStateChanged(object sender, EventArgs e){ private void FormBrowser_ResizeEnd(object sender, EventArgs e){ // also triggers when the window moves
Config.IsMaximized = WindowState != FormWindowState.Normal; if (!isLoaded)return;
FormBrowser_ResizeEnd(sender,e);
if (Location.X != -32000){
Config.IsMaximized = WindowState == FormWindowState.Maximized;
Config.WindowLocation = Location;
Config.WindowSize = Size;
Config.Save();
}
}
private void trayIcon_Click(object sender, EventArgs e){
isLoaded = false;
Show();
SetupWindow();
trayIcon.Visible = false;
} }
// callback handlers // callback handlers
public void InvokeSafe(Action func){ public void InvokeSafe(Action func){
if (InvokeRequired){ ControlExtensions.InvokeSafe(this,func);
Invoke(func);
}
else{
func();
}
} }
public void OpenSettings(){ public void OpenSettings(){
@@ -118,8 +143,21 @@ namespace TweetDick.Core{
currentFormSettings.BringToFront(); currentFormSettings.BringToFront();
} }
else{ else{
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
currentFormSettings = new FormSettings(this); 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); ShowChildForm(currentFormSettings);
} }
} }
@@ -138,5 +176,24 @@ namespace TweetDick.Core{
public void OnTweetPopup(TweetNotification tweet){ public void OnTweetPopup(TweetNotification tweet){
notification.ShowNotification(tweet); notification.ShowNotification(tweet);
} }
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 { sealed partial class FormNotification {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
@@ -26,7 +28,7 @@
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.panelBrowser = new System.Windows.Forms.Panel(); this.panelBrowser = new System.Windows.Forms.Panel();
this.timerProgress = new System.Windows.Forms.Timer(this.components); this.timerProgress = new System.Windows.Forms.Timer(this.components);
this.progressBarTimer = new TweetDick.Core.Controls.FlatProgressBar(); this.progressBarTimer = new FlatProgressBar();
this.SuspendLayout(); this.SuspendLayout();
// //
// panelBrowser // panelBrowser
@@ -65,7 +67,7 @@
this.Controls.Add(this.progressBarTimer); this.Controls.Add(this.progressBarTimer);
this.Controls.Add(this.panelBrowser); this.Controls.Add(this.panelBrowser);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Location = new System.Drawing.Point(32000, 32000); this.Location = new System.Drawing.Point(-32000, -32000);
this.Name = "FormNotification"; this.Name = "FormNotification";
this.ShowInTaskbar = false; this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;

View File

@@ -1,15 +1,17 @@
using System.Windows.Forms; using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using System.Drawing; using TweetDck.Configuration;
using System; using TweetDck.Core.Handling;
using System.Collections.Generic; using TweetDck.Resources;
using TweetDick.Core.Handling;
using TweetDick.Configuration;
using TweetDick.Resources;
namespace TweetDick.Core{ namespace TweetDck.Core{
sealed partial class FormNotification : Form{ sealed partial class FormNotification : Form{
public Func<bool> CanMoveWindow = () => true;
private readonly Form owner; private readonly Form owner;
private readonly ChromiumWebBrowser browser; private readonly ChromiumWebBrowser browser;
@@ -31,7 +33,7 @@ namespace TweetDick.Core{
notificationJS = ScriptLoader.LoadResource("notification.js"); 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; browser.FrameLoadEnd += Browser_FrameLoadEnd;
if (bridge != null){ if (bridge != null){
@@ -39,6 +41,8 @@ namespace TweetDick.Core{
} }
panelBrowser.Controls.Add(browser); panelBrowser.Controls.Add(browser);
Disposed += (sender, args) => browser.Dispose();
} }
public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){} public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){}
@@ -49,6 +53,14 @@ namespace TweetDick.Core{
} }
} }
protected override void WndProc(ref Message m){
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
return;
}
base.WndProc(ref m);
}
public void ShowNotification(TweetNotification notification){ public void ShowNotification(TweetNotification notification){
MoveToVisibleLocation(); MoveToVisibleLocation();
@@ -76,7 +88,7 @@ namespace TweetDick.Core{
public void HideNotification(){ public void HideNotification(){
browser.LoadHtml("","about:blank"); browser.LoadHtml("","about:blank");
Location = new Point(32000,32000); Location = new Point(-32000,-32000);
TopMost = false; TopMost = false;
timerProgress.Stop(); timerProgress.Stop();
} }
@@ -99,6 +111,15 @@ namespace TweetDick.Core{
UserConfig config = Program.UserConfig; UserConfig config = Program.UserConfig;
Screen screen = Screen.FromControl(owner); 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){ if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
screen = Screen.AllScreens[config.NotificationDisplay-1]; screen = Screen.AllScreens[config.NotificationDisplay-1];
} }
@@ -158,21 +179,5 @@ namespace TweetDick.Core{
e.Cancel = true; 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;
}
}
} }
} }

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)){
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(parameters.UnfilteredLinkUrl,TextDataFormat.Text);
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.Text);
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

@@ -1,38 +1,41 @@
using CefSharp; using CefSharp;
namespace TweetDick.Core.Handling{ namespace TweetDck.Core.Handling{
class ContextMenuHandler : IContextMenuHandler{ class ContextMenuBrowser : ContextMenuBase{
private const int MenuSettings = 26500; private const int MenuSettings = 26600;
private const int MenuAbout = 26501; private const int MenuAbout = 26601;
private readonly FormBrowser form; private readonly FormBrowser form;
public ContextMenuHandler(FormBrowser form){ public ContextMenuBrowser(FormBrowser form){
this.form = form; this.form = form;
} }
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Remove(CefMenuCommand.Back); model.Remove(CefMenuCommand.Back);
model.Remove(CefMenuCommand.Forward); model.Remove(CefMenuCommand.Forward);
model.Remove(CefMenuCommand.Print); model.Remove(CefMenuCommand.Print);
model.Remove(CefMenuCommand.ViewSource); model.Remove(CefMenuCommand.ViewSource);
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){ RemoveSeparatorIfFirst(model);
model.RemoveAt(model.Count-1);
} base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
model.AddItem(CefMenuCommand.Reload,"Reload"); model.AddItem(CefMenuCommand.Reload,"Reload");
model.AddSeparator(); model.AddSeparator();
if (TweetNotification.IsReady){ if (TweetNotification.IsReady){
model.AddItem((CefMenuCommand)MenuSettings,"Settings"); model.AddItem((CefMenuCommand)MenuSettings,"Settings");
model.AddSeparator();
} }
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName); model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
} }
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
if (base.OnContextMenuCommand(browserControl,browser,frame,parameters,commandId,eventFlags)){
return true;
}
switch((int)commandId){ switch((int)commandId){
case MenuSettings: case MenuSettings:
form.InvokeSafe(() => { form.InvokeSafe(() => {
@@ -51,11 +54,5 @@ namespace TweetDick.Core.Handling{
return false; 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,16 @@
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);
}
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
return false;
}
}
}

View File

@@ -1,6 +1,6 @@
using System.Diagnostics; using TweetDck.Core.Utils;
namespace TweetDick.Core.Handling{ namespace TweetDck.Core.Handling{
class TweetDeckBridge{ class TweetDeckBridge{
private readonly FormBrowser form; private readonly FormBrowser form;
@@ -10,6 +10,24 @@ namespace TweetDick.Core.Handling{
} }
} }
public string VersionTag{
get{
return Program.VersionTag;
}
}
public bool UpdateCheckEnabled{
get{
return Program.UserConfig.EnableUpdateCheck;
}
}
public string DismissedVersionTag{
get{
return Program.UserConfig.DismissedUpdate ?? string.Empty;
}
}
public TweetDeckBridge(FormBrowser form){ public TweetDeckBridge(FormBrowser form){
this.form = form; this.form = form;
} }
@@ -38,12 +56,25 @@ namespace TweetDick.Core.Handling{
}); });
} }
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){ public void OpenBrowser(string url){
Process.Start(url); BrowserUtils.OpenExternalBrowser(url);
} }
public void Log(string data){ public void Log(string data){
Debug.WriteLine(data); System.Diagnostics.Debug.WriteLine(data);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Text; using System.Text;
namespace TweetDick.Core.Handling{ namespace TweetDck.Core.Handling{
sealed class TweetNotification{ sealed class TweetNotification{
private static string FontSizeClass { get; set; } private static string FontSizeClass { get; set; }
private static string HeadTag { get; set; } private static string HeadTag { get; set; }
@@ -17,10 +17,10 @@ namespace TweetDick.Core.Handling{
StringBuilder build = new StringBuilder(); 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(@"<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(@"<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(@"<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(@"<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='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(@"<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(@"</a>");
build.Append(@"</header>"); 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>"); 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>");

View File

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

View File

@@ -1,20 +1,43 @@
using System.Text; using System;
using System.Text;
using System.Windows.Forms; 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{ sealed partial class FormAbout : Form{
private const string GitHubLink = "https://github.com/chylex/TweetDuck";
public FormAbout(){ public FormAbout(){
InitializeComponent(); InitializeComponent();
Text = "About "+Program.BrandName; Text = "About "+Program.BrandName+" "+Program.VersionTag;
StringBuilder build = new StringBuilder(); 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(@"\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(@"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()); 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 { partial class FormBackgroundWork {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.

View File

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

View File

@@ -1,4 +1,4 @@
namespace TweetDick.Core.Other { namespace TweetDck.Core.Other {
sealed partial class FormSettings { sealed partial class FormSettings {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.
@@ -23,7 +23,6 @@
/// the contents of this method with the code editor. /// the contents of this method with the code editor.
/// </summary> /// </summary>
private void InitializeComponent() { private void InitializeComponent() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormSettings));
this.groupNotificationLocation = new System.Windows.Forms.GroupBox(); this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
this.labelDisplay = new System.Windows.Forms.Label(); this.labelDisplay = new System.Windows.Forms.Label();
this.comboBoxDisplay = new System.Windows.Forms.ComboBox(); this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
@@ -34,16 +33,23 @@
this.radioLocBL = new System.Windows.Forms.RadioButton(); this.radioLocBL = new System.Windows.Forms.RadioButton();
this.radioLocTR = new System.Windows.Forms.RadioButton(); this.radioLocTR = new System.Windows.Forms.RadioButton();
this.radioLocTL = 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.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.checkMinimizeTray = new System.Windows.Forms.CheckBox();
this.groupNotificationDuration = new System.Windows.Forms.GroupBox(); this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
this.radioDurVeryLong = new System.Windows.Forms.RadioButton(); this.radioDurVeryLong = new System.Windows.Forms.RadioButton();
this.radioDurLong = new System.Windows.Forms.RadioButton(); this.radioDurLong = new System.Windows.Forms.RadioButton();
this.radioDurMedium = new System.Windows.Forms.RadioButton(); this.radioDurMedium = new System.Windows.Forms.RadioButton();
this.radioDurShort = new System.Windows.Forms.RadioButton(); this.radioDurShort = new System.Windows.Forms.RadioButton();
this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.groupNotificationLocation.SuspendLayout(); this.groupNotificationLocation.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.groupNotificationDuration.SuspendLayout();
this.tableLayout.SuspendLayout(); this.tableLayout.SuspendLayout();
this.tableColumn2Panel.SuspendLayout();
this.groupUserInterface.SuspendLayout();
this.groupNotificationDuration.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// groupNotificationLocation // groupNotificationLocation
@@ -181,6 +187,70 @@
this.radioLocTL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged); this.radioLocTL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocTL.Click += new System.EventHandler(this.radioLoc_Click); 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.checkUpdateNotifications);
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Controls.Add(this.checkMinimizeTray);
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";
//
// checkNotificationTimer
//
this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(6, 43);
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);
//
// checkMinimizeTray
//
this.checkMinimizeTray.AutoSize = true;
this.checkMinimizeTray.Location = new System.Drawing.Point(6, 20);
this.checkMinimizeTray.Name = "checkMinimizeTray";
this.checkMinimizeTray.Size = new System.Drawing.Size(102, 17);
this.checkMinimizeTray.TabIndex = 0;
this.checkMinimizeTray.Text = "Minimize to Tray";
this.checkMinimizeTray.UseVisualStyleBackColor = true;
this.checkMinimizeTray.CheckedChanged += new System.EventHandler(this.checkMinimizeTray_CheckedChanged);
//
// groupNotificationDuration // groupNotificationDuration
// //
this.groupNotificationDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) this.groupNotificationDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
@@ -189,10 +259,10 @@
this.groupNotificationDuration.Controls.Add(this.radioDurLong); this.groupNotificationDuration.Controls.Add(this.radioDurLong);
this.groupNotificationDuration.Controls.Add(this.radioDurMedium); this.groupNotificationDuration.Controls.Add(this.radioDurMedium);
this.groupNotificationDuration.Controls.Add(this.radioDurShort); 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.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 118); this.groupNotificationDuration.Size = new System.Drawing.Size(183, 119);
this.groupNotificationDuration.TabIndex = 1; this.groupNotificationDuration.TabIndex = 2;
this.groupNotificationDuration.TabStop = false; this.groupNotificationDuration.TabStop = false;
this.groupNotificationDuration.Text = "Notification Duration"; this.groupNotificationDuration.Text = "Notification Duration";
// //
@@ -248,22 +318,16 @@
this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged); this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click); this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click);
// //
// tableLayout // checkUpdateNotifications
// //
this.tableLayout.ColumnCount = 2; this.checkUpdateNotifications.AutoSize = true;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 67);
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.tableLayout.Controls.Add(this.groupNotificationLocation, 0, 0); this.checkUpdateNotifications.Size = new System.Drawing.Size(122, 17);
this.tableLayout.Controls.Add(this.groupNotificationDuration, 1, 0); this.checkUpdateNotifications.TabIndex = 5;
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; this.checkUpdateNotifications.Text = "Update Notifications";
this.tableLayout.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize; this.checkUpdateNotifications.UseVisualStyleBackColor = true;
this.tableLayout.Location = new System.Drawing.Point(0, 0); this.checkUpdateNotifications.CheckedChanged += new System.EventHandler(this.checkUpdateNotifications_CheckedChanged);
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 // FormSettings
// //
@@ -272,7 +336,7 @@
this.ClientSize = new System.Drawing.Size(384, 282); this.ClientSize = new System.Drawing.Size(384, 282);
this.Controls.Add(this.tableLayout); this.Controls.Add(this.tableLayout);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = TweetDick.Properties.Resources.icon; this.Icon = global::TweetDck.Properties.Resources.icon;
this.MaximizeBox = false; this.MaximizeBox = false;
this.MinimizeBox = false; this.MinimizeBox = false;
this.Name = "FormSettings"; this.Name = "FormSettings";
@@ -281,9 +345,12 @@
this.groupNotificationLocation.ResumeLayout(false); this.groupNotificationLocation.ResumeLayout(false);
this.groupNotificationLocation.PerformLayout(); this.groupNotificationLocation.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit(); ((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.ResumeLayout(false);
this.groupNotificationDuration.PerformLayout(); this.groupNotificationDuration.PerformLayout();
this.tableLayout.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@@ -298,13 +365,18 @@
private System.Windows.Forms.RadioButton radioLocTL; private System.Windows.Forms.RadioButton radioLocTL;
private System.Windows.Forms.Label labelEdgeDistance; private System.Windows.Forms.Label labelEdgeDistance;
private System.Windows.Forms.TrackBar trackBarEdgeDistance; 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.GroupBox groupNotificationDuration;
private System.Windows.Forms.RadioButton radioDurVeryLong; private System.Windows.Forms.RadioButton radioDurVeryLong;
private System.Windows.Forms.RadioButton radioDurLong; private System.Windows.Forms.RadioButton radioDurLong;
private System.Windows.Forms.RadioButton radioDurMedium; private System.Windows.Forms.RadioButton radioDurMedium;
private System.Windows.Forms.RadioButton radioDurShort; private System.Windows.Forms.RadioButton radioDurShort;
private System.Windows.Forms.Label labelDisplay; private System.Windows.Forms.CheckBox checkMinimizeTray;
private System.Windows.Forms.ComboBox comboBoxDisplay; private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.TableLayoutPanel tableLayout; private System.Windows.Forms.CheckBox checkUpdateNotifications;
} }
} }

View File

@@ -1,9 +1,9 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDick.Configuration; using TweetDck.Configuration;
using TweetDick.Core.Handling; using TweetDck.Core.Handling;
namespace TweetDick.Core.Other{ namespace TweetDck.Core.Other{
sealed partial class FormSettings : Form{ sealed partial class FormSettings : Form{
private static UserConfig Config{ private static UserConfig Config{
get{ get{
@@ -20,8 +20,7 @@ namespace TweetDick.Core.Other{
Text = Program.BrandName+" Settings"; Text = Program.BrandName+" Settings";
notification = new FormNotification(browserForm,false); notification = new FormNotification(browserForm,false){ CanMoveWindow = () => radioLocCustom.Checked };
notification.Show(this);
notification.Move += (sender, args) => { notification.Move += (sender, args) => {
if (radioLocCustom.Checked){ if (radioLocCustom.Checked){
@@ -29,6 +28,8 @@ namespace TweetDick.Core.Other{
} }
}; };
notification.Show(this);
switch(Config.NotificationPosition){ switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break; case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break; case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
@@ -53,6 +54,9 @@ namespace TweetDick.Core.Other{
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay); comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay);
trackBarEdgeDistance.Value = Config.NotificationEdgeDistance; trackBarEdgeDistance.Value = Config.NotificationEdgeDistance;
checkMinimizeTray.Checked = Config.MinimizeToTray;
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
} }
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){ private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
@@ -114,5 +118,24 @@ namespace TweetDick.Core.Other{
notification.ShowNotificationForSettings(true); notification.ShowNotificationForSettings(true);
} }
private void checkMinimizeTray_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.MinimizeToTray = checkMinimizeTray.Checked;
}
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 = global::TweetDck.Properties.Resources.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();
});
}
}
}

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 { partial class FormMigrationQuestion {
/// <summary> /// <summary>
/// Required designer variable. /// Required designer variable.

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
using System.Linq; using System.Linq;
using Microsoft.Win32; using Microsoft.Win32;
namespace TweetDick.Migration.Helpers{ namespace TweetDck.Migration.Helpers{
static class ProgramRegistrySearch{ static class ProgramRegistrySearch{
public static string FindByDisplayName(string displayName){ public static string FindByDisplayName(string displayName){
Predicate<RegistryKey> predicate = key => displayName.Equals(key.GetValue("DisplayName") as string,StringComparison.OrdinalIgnoreCase); 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{ enum MigrationDecision{
/// <summary> /// <summary>
/// Copies the important files and then deletes the TweetDeck folder. /// Copies the important files and then deletes the TweetDeck folder.

View File

@@ -1,21 +1,26 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDick.Core.Other; using Microsoft.Win32;
using TweetDick.Migration.Helpers; using TweetDck.Core.Other;
using TweetDck.Migration.Helpers;
namespace TweetDick.Migration{ namespace TweetDck.Migration{
static class MigrationManager{ static class MigrationManager{
private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"twitter"); private static readonly string TweetDeckPathParent = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"twitter");
private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent,"TweetDeck"); private static readonly string TweetDeckPath = Path.Combine(TweetDeckPathParent,"TweetDeck");
public static void Run(){ public static void Run(){
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){ if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
FormMigrationQuestion formQuestion = new FormMigrationQuestion(); MigrationDecision decision;
MigrationDecision decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
using(FormMigrationQuestion formQuestion = new FormMigrationQuestion()){
decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
}
switch(decision){ switch(decision){
case MigrationDecision.MigratePurge: case MigrationDecision.MigratePurge:
@@ -25,12 +30,13 @@ namespace TweetDick.Migration{
formWait.ShowWorkDialog(() => { formWait.ShowWorkDialog(() => {
if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => { if (!BeginMigration(decision,ex => formWait.Invoke(new Action(() => {
if (ex != null){
MessageBox.Show(ex.ToString()); // TODO
}
formWait.Close(); formWait.Close();
if (ex != null){
Program.HandleException("An unexpected exception has occurred during the migration process.",ex);
return;
}
Program.UserConfig.IgnoreMigration = true; Program.UserConfig.IgnoreMigration = true;
Program.UserConfig.Save(); Program.UserConfig.Save();
})))){ })))){
@@ -46,6 +52,17 @@ namespace TweetDick.Migration{
break; break;
} }
} }
else if (!Program.UserConfig.IgnoreUninstallCheck){
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
if (guid != null && MessageBox.Show("TweetDeck is still installed on your computer, do you want to uninstall it?","Uninstall TweetDeck",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
RunUninstaller(guid,0);
CleanupTweetDeck();
}
Program.UserConfig.IgnoreUninstallCheck = true;
Program.UserConfig.Save();
}
} }
private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){ private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){
@@ -98,14 +115,10 @@ namespace TweetDick.Migration{
} }
if (decision == MigrationDecision.MigratePurge){ if (decision == MigrationDecision.MigratePurge){
// update the lnk files wherever possible (desktop icons, pinned taskbar) // update the lnk files wherever possible (desktop icons, pinned taskbar, start menu)
string[] locations = { foreach(string location in GetLnkDirectories()){
Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), if (location == string.Empty)continue;
Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory),
Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar")
};
foreach(string location in locations){
string linkFile = Path.Combine(location,"TweetDeck.lnk"); string linkFile = Path.Combine(location,"TweetDeck.lnk");
if (File.Exists(linkFile)){ if (File.Exists(linkFile)){
@@ -115,7 +128,18 @@ namespace TweetDick.Migration{
lnk.SetComment(Program.BrandName); // TODO add a tagline lnk.SetComment(Program.BrandName); // TODO add a tagline
lnk.Save(); lnk.Save();
File.Move(linkFile,Path.Combine(location,Program.BrandName+".lnk")); string renamed = Path.Combine(location,Program.BrandName+".lnk");
try{
if (!File.Exists(renamed)){
File.Move(linkFile,renamed);
}
else{
File.Delete(linkFile);
}
}catch{
// eh, too bad
}
} }
} }
@@ -125,13 +149,12 @@ namespace TweetDick.Migration{
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck"); string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
if (guid != null){ if (guid != null){
Process uninstaller = Process.Start("msiexec.exe","/x"+guid+" /quiet /qn"); RunUninstaller(guid,5000);
if (uninstaller != null){
uninstaller.WaitForExit();
}
} }
// registry cleanup
CleanupTweetDeck();
// migration finished like a boss // migration finished like a boss
} }
}); });
@@ -149,5 +172,41 @@ namespace TweetDick.Migration{
}catch(DirectoryNotFoundException){ }catch(DirectoryNotFoundException){
} }
} }
private static IEnumerable<string> GetLnkDirectories(){
yield return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
yield return Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar");
/* already handled by the installer
string startMenu = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string[] sub = Directory.GetDirectories(startMenu);
if (sub.Length > 0){
yield return Path.Combine(startMenu,sub[0],"TweetDeck");
}
*/
}
private static void RunUninstaller(string guid, int timeout){
Process uninstaller = Process.Start("msiexec.exe","/x "+guid+" /quiet /qn");
if (uninstaller != null){
if (timeout > 0){
uninstaller.WaitForExit(timeout); // it appears that the process is restarted or something that triggers this, but it shouldn't be a problem
}
uninstaller.Close();
}
}
private static void CleanupTweetDeck(){
try{
Registry.CurrentUser.DeleteSubKeyTree(@"Software\Twitter\TweetDeck",true);
Registry.CurrentUser.DeleteSubKey(@"Software\Twitter"); // only if empty
}catch(Exception){
// not found or too bad
}
}
} }
} }

View File

@@ -1,14 +1,18 @@
using CefSharp; using System;
using System; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDick.Configuration; using CefSharp;
using TweetDick.Core; using TweetDck.Configuration;
using TweetDick.Migration; using TweetDck.Core;
using TweetDck.Migration;
using TweetDck.Core.Utils;
namespace TweetDick{ [assembly: CLSCompliant(true)]
namespace TweetDck{
static class Program{ static class Program{
#if DUCK #if DUCK
public const string BrandName = "TweetDuck"; public const string BrandName = "TweetDuck";
@@ -18,28 +22,19 @@ namespace TweetDick{
public const string Website = "http://tweetdick.chylex.com"; public const string Website = "http://tweetdick.chylex.com";
#endif #endif
public const string VersionTag = "1.1";
public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName); public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName);
private static readonly LockManager LockManager; private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
public static UserConfig UserConfig { get; private set; } public static UserConfig UserConfig { get; private set; }
private static string HeaderAcceptLanguage{ public static string LogFile{
get{ get{
string culture = CultureInfo.CurrentCulture.Name; return Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"td-log.txt");
if (culture == "en"){
return "en-us,en";
}
else{
return culture.ToLowerInvariant()+",en;q=0.9";
}
} }
} }
static Program(){
LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibrary(string name); public static extern IntPtr LoadLibrary(string name);
@@ -73,8 +68,8 @@ namespace TweetDick{
}; };
Cef.Initialize(new CefSettings{ Cef.Initialize(new CefSettings{
AcceptLanguageList = HeaderAcceptLanguage, AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
UserAgent = BrandName+" "+Application.ProductVersion, UserAgent = BrowserUtils.HeaderUserAgent,
Locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName, Locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
CachePath = StoragePath, CachePath = StoragePath,
#if !DEBUG #if !DEBUG
@@ -82,13 +77,54 @@ namespace TweetDick{
#endif #endif
}); });
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
Exception ex = args.ExceptionObject as Exception;
if (ex != null){
HandleException("An unhandled exception has occurred.",ex);
}
};
Application.ApplicationExit += (sender, args) => { Application.ApplicationExit += (sender, args) => {
UserConfig.Save(); UserConfig.Save();
LockManager.Unlock(); LockManager.Unlock();
Cef.Shutdown(); 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(LogFile);
}
}
public static void Log(string data){
StringBuilder build = new StringBuilder();
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(LogFile,build.ToString(),Encoding.UTF8);
}catch{
// oops
}
} }
} }
} }

View File

@@ -1,12 +1,13 @@
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using TweetDick; using System.Resources;
using TweetDck;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle(Program.BrandName)] [assembly: AssemblyTitle("TweetDeck client for Windows")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("TweetDeck client for Windows")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct(Program.BrandName)] [assembly: AssemblyProduct(Program.BrandName)]
@@ -32,5 +33,7 @@ using TweetDick;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.1.0")] [assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("0.9.1.0")] [assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: NeutralResourcesLanguageAttribute("en")]

View File

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

@@ -3,11 +3,11 @@ using System.IO;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
namespace TweetDick.Resources{ namespace TweetDck.Resources{
static class ScriptLoader{ static class ScriptLoader{
public static string LoadResource(string name){ public static string LoadResource(string name){
try{ try{
return File.ReadAllText(name,Encoding.UTF8); return File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,name),Encoding.UTF8);
}catch(Exception ex){ }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); 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; return null;

View File

@@ -5,7 +5,7 @@
var fontSizeClasses = [ "txt-base-smallest", "txt-base-small", "txt-base-medium", "txt-base-large", "txt-base-largest" ]; 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; var isInitialized = false;
@@ -15,24 +15,29 @@
var prevFontSizeClass; var prevFontSizeClass;
// //
// Function: Initializes TweetDick events. Called after the website app is loaded. // Variable: Current timeout ID for update checking.
// //
var initializeTweetDick = function(){ var updateCheckTimeoutID;
//
// Function: Initializes TweetD*ck events. Called after the website app is loaded.
//
var initializeTweetDck = function(){
// Settings button hook // Settings button hook
$("[data-action='settings-menu']").click(function(){ $("[data-action='settings-menu']").click(function(){
setTimeout(function(){ setTimeout(function(){
var menu = $(".js-dropdown-content").children("ul").first(); var menu = $(".js-dropdown-content").children("ul").first();
if (menu.length == 0)return; 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+'</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(); $TD.openSettingsMenu();
}); });
tweetDickBtn.hover(function(){ tweetDckBtn.hover(function(){
$(this).addClass("is-selected"); $(this).addClass("is-selected");
},function(){ },function(){
$(this).removeClass("is-selected"); $(this).removeClass("is-selected");
@@ -57,7 +62,7 @@
refreshColumnObservers(); refreshColumnObservers();
})(); })();
// Popup notifications // Force popup notification settings
window.TD.controller.notifications.hasNotifications = function(){ window.TD.controller.notifications.hasNotifications = function(){
return true; return true;
}; };
@@ -66,6 +71,9 @@
return true; return true;
}; };
// Run update check
runUpdateCheck();
// Finish init // Finish init
isInitialized = true; isInitialized = true;
}; };
@@ -83,7 +91,7 @@
mid = mid.children(".js-column-scroller").first(); mid = mid.children(".js-column-scroller").first();
var container = mid.children(".js-chirp-container").first(); var container = mid.children(".js-chirp-container").first();
if (container.length == 0)return; if (container.length === 0)return;
var scroller = container.parent(); var scroller = container.parent();
@@ -100,7 +108,7 @@
Array.prototype.forEach.call(mutations,function(mutation){ Array.prototype.forEach.call(mutations,function(mutation){
Array.prototype.forEach.call(mutation.addedNodes,function(node){ Array.prototype.forEach.call(mutation.addedNodes,function(node){
if (node.tagName != "ARTICLE")return; if (node.tagName !== "ARTICLE")return;
onNewTweet(column,node); onNewTweet(column,node);
}); });
@@ -119,13 +127,20 @@
var html = $(tweet.outerHTML); var html = $(tweet.outerHTML);
var body = html.find(".tweet-body").first(); var body = html.find(".tweet-body").first();
if (html.find(".icon-reply").length > 0){
return; // ignore sent messages
}
body.children("div.js-quote-detail").each(function(){ body.children("div.js-quote-detail").each(function(){
$(this).html("(quoted tweet)"); $(this).html("(quoted tweet)");
}); });
body.children().not("p,div.js-quote-detail").remove(); body.children().not("p,span,div.js-quote-detail").remove();
$TD.onTweetPopup(html.html(),html.find(".js-tweet-text:first").text().length); // TODO column & remove pic links from text() var characters = html.find(".js-tweet-text:first").text().length;
if (characters == 0)return;
$TD.onTweetPopup(html.html(),characters); // TODO column & remove pic links from text()
}; };
// //
@@ -155,7 +170,115 @@
}; };
// //
// Block: Observe the app <div> element and initialize TweetDick whenever possible. // 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>"
];
$("h1.app-title").after(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"
});
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: Observe the app <div> element and initialize TweetD*ck whenever possible.
// //
var app = $("body").children(".js-app"); var app = $("body").children(".js-app");
@@ -164,7 +287,7 @@
isInitialized = false; isInitialized = false;
} }
else if (!isInitialized && !app.hasClass("is-hidden")){ else if (!isInitialized && !app.hasClass("is-hidden")){
initializeTweetDick(); initializeTweetDck();
} }
}).observe(app[0],{ }).observe(app[0],{
attributes: true, attributes: true,
@@ -213,8 +336,9 @@
if (urlWait)return; if (urlWait)return;
var me = $(this); var me = $(this);
var rel = me.attr("rel");
if (!me.is(".link-complex") && !(me.attr("rel") == "mediaPreview" && me.closest("#open-modal").length == 0)){ if (!me.is(".link-complex") && !(rel === "mediaPreview" && me.closest("#open-modal").length === 0) && rel !== "list"){
$TD.openBrowser(me.attr("href")); $TD.openBrowser(me.attr("href"));
} }
@@ -229,4 +353,21 @@
onUrlOpened(); onUrlOpened();
}; };
})(); })();
//
// 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();
}
});
//
// Block: Setup global functions.
//
window.TDGF_runUpdateCheck = runUpdateCheck;
})($,$TD); })($,$TD);

View File

@@ -9,7 +9,7 @@
<ProjectGuid>{2389A7CD-E0D3-4706-8294-092929A33A2D}</ProjectGuid> <ProjectGuid>{2389A7CD-E0D3-4706-8294-092929A33A2D}</ProjectGuid>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TweetDick</RootNamespace> <RootNamespace>TweetDck</RootNamespace>
<AssemblyName Condition=" '$(Configuration)' == 'Debug' ">TweetDick</AssemblyName> <AssemblyName Condition=" '$(Configuration)' == 'Debug' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Dick' ">TweetDick</AssemblyName> <AssemblyName Condition=" '$(Configuration)' == 'Release Dick' ">TweetDick</AssemblyName>
<AssemblyName Condition=" '$(Configuration)' == 'Release Duck' ">TweetDuck</AssemblyName> <AssemblyName Condition=" '$(Configuration)' == 'Release Duck' ">TweetDuck</AssemblyName>
@@ -77,13 +77,15 @@
<ItemGroup> <ItemGroup>
<Compile Include="Configuration\LockManager.cs" /> <Compile Include="Configuration\LockManager.cs" />
<Compile Include="Configuration\UserConfig.cs" /> <Compile Include="Configuration\UserConfig.cs" />
<Compile Include="Core\Controls\ControlExtensions.cs" />
<Compile Include="Core\Controls\FlatProgressBar.cs"> <Compile Include="Core\Controls\FlatProgressBar.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="Core\Controls\FlatProgressBar.Designer.cs"> <Compile Include="Core\Controls\FlatProgressBar.Designer.cs">
<DependentUpon>FlatProgressBar.cs</DependentUpon> <DependentUpon>FlatProgressBar.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Handling\ContextMenuHandler.cs" /> <Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" />
<Compile Include="Core\FormBrowser.cs"> <Compile Include="Core\FormBrowser.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -96,6 +98,7 @@
<Compile Include="Core\FormNotification.Designer.cs"> <Compile Include="Core\FormNotification.Designer.cs">
<DependentUpon>FormNotification.cs</DependentUpon> <DependentUpon>FormNotification.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Handling\ContextMenuNotification.cs" />
<Compile Include="Core\Handling\TweetNotification.cs" /> <Compile Include="Core\Handling\TweetNotification.cs" />
<Compile Include="Core\Controls\RichTextLabel.cs"> <Compile Include="Core\Controls\RichTextLabel.cs">
<SubType>Component</SubType> <SubType>Component</SubType>
@@ -122,6 +125,14 @@
<Compile Include="Core\Other\FormSettings.Designer.cs"> <Compile Include="Core\Other\FormSettings.Designer.cs">
<DependentUpon>FormSettings.cs</DependentUpon> <DependentUpon>FormSettings.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Core\Other\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormUpdateDownload.Designer.cs">
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\UpdateInfo.cs" />
<Compile Include="Migration\FormMigrationQuestion.cs"> <Compile Include="Migration\FormMigrationQuestion.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -220,7 +231,9 @@
xcopy "$(ProjectDir)LICENSE" "$(TargetDir)" /Y xcopy "$(ProjectDir)LICENSE" "$(TargetDir)" /Y
del "$(TargetDir)LICENSE.txt" del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE" "LICENSE.txt" ren "$(TargetDir)LICENSE" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y</PostBuildEvent> 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> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. 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 # Visual Studio 2013
VisualStudioVersion = 12.0.40629.0 VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

4
_postbuild.bat Normal file
View File

@@ -0,0 +1,4 @@
del "bin\x86\Release Dick\*.xml"
del "bin\x86\Release Dick\devtools_resources.pak"
del "bin\x86\Release Duck\*.xml"
del "bin\x86\Release Duck\devtools_resources.pak"