mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-09-14 19:32:10 +02:00
Compare commits
70 Commits
Author | SHA1 | Date | |
---|---|---|---|
b6b26142f8 | |||
4ee99376fd | |||
b0261342ff | |||
87fd2a521e | |||
334793c6f6 | |||
3e70d991bb | |||
feec96fc5c | |||
765984709e | |||
c7c9931f68 | |||
ae64573510 | |||
d675af5aa4 | |||
9480d17cfc | |||
5ac1df2283 | |||
20119db883 | |||
a4006deb8c | |||
25fa3cefab | |||
bb5161eb34 | |||
1bfc403a98 | |||
720d10e543 | |||
30c117672e | |||
6d1b5c77d1 | |||
d1cbf608e0 | |||
7e3014c52d | |||
82beb1f5a7 | |||
657dc81300 | |||
8e22192dd3 | |||
dc0b7d58e3 | |||
6919e5bdb0 | |||
9728a62efa | |||
276e070759 | |||
fadea54f8d | |||
523d340ade | |||
96fa7efb66 | |||
03591f8317 | |||
28cc60d007 | |||
1fa69bdb3b | |||
11f5f9b72e | |||
61f6543041 | |||
0c9ab32f37 | |||
fde984d02b | |||
f23db31306 | |||
8dce99b8b3 | |||
342ac51cda | |||
ba31f7ae01 | |||
8d0fa030f8 | |||
d030a79c81 | |||
6690efc4d9 | |||
afa8098463 | |||
c064e579d2 | |||
01dc4e4714 | |||
6fbc246b12 | |||
1efe2a02f7 | |||
ab2ab06f60 | |||
a71d889682 | |||
2252d85b27 | |||
3f09100177 | |||
6b79c89f42 | |||
5f249d4603 | |||
fa64309909 | |||
c46aafdab6 | |||
3e9c397494 | |||
47935165db | |||
be77f753e7 | |||
2c30613279 | |||
d83eaec987 | |||
e6f199a224 | |||
6636a2aa9e | |||
221bdc58fe | |||
e48a068e9d | |||
3371c31e63 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -17,9 +17,13 @@
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
bld/*
|
||||
!bld/gen_full.iss
|
||||
!bld/gen_port.iss
|
||||
!bld/gen_upd.iss
|
||||
!bld/Resources
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
|
@@ -58,7 +58,7 @@ namespace TweetDck.Configuration{
|
||||
Process foundProcess = Process.GetProcessById(pid);
|
||||
|
||||
using(Process currentProcess = Process.GetCurrentProcess()){
|
||||
if (foundProcess.ProcessName == currentProcess.ProcessName){
|
||||
if (foundProcess.ProcessName == currentProcess.ProcessName || foundProcess.MainWindowTitle == Program.BrandName){
|
||||
LockingProcess = foundProcess;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Controls{
|
||||
static class ControlExtensions{
|
||||
@@ -34,5 +35,21 @@ namespace TweetDck.Core.Controls{
|
||||
trackBar.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetElevated(this Button button){
|
||||
button.Text = " "+button.Text;
|
||||
button.FlatStyle = FlatStyle.System;
|
||||
NativeMethods.SendMessage(button.Handle, NativeMethods.BCM_SETSHIELD, 0, new IntPtr(1));
|
||||
}
|
||||
|
||||
public static void EnableMultilineShortcuts(this TextBox textBox){
|
||||
textBox.KeyDown += (sender, args) => {
|
||||
if (args.Control && args.KeyCode == Keys.A){
|
||||
((TextBox)sender).SelectAll();
|
||||
args.SuppressKeyPress = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -62,6 +62,7 @@ namespace TweetDck.Core{
|
||||
public bool FreezeTimer { get; set; }
|
||||
public bool ContextMenuOpen { get; set; }
|
||||
public string CurrentUrl { get; private set; }
|
||||
public string CurrentQuotedTweetUrl { get; set; }
|
||||
|
||||
public EventHandler Initialized;
|
||||
private bool isInitialized;
|
||||
@@ -225,7 +226,7 @@ namespace TweetDck.Core{
|
||||
tweetQueue.Enqueue(notification);
|
||||
UpdateTitle();
|
||||
|
||||
if (!timerProgress.Enabled){
|
||||
if (totalTime == 0){
|
||||
LoadNextNotification();
|
||||
}
|
||||
}
|
||||
@@ -248,6 +249,7 @@ namespace TweetDck.Core{
|
||||
Location = new Point(-32000, -32000);
|
||||
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
||||
timerProgress.Stop();
|
||||
totalTime = 0;
|
||||
|
||||
StopMouseHook();
|
||||
}
|
||||
@@ -276,6 +278,7 @@ namespace TweetDck.Core{
|
||||
|
||||
private void LoadTweet(TweetNotification tweet){
|
||||
CurrentUrl = tweet.Url;
|
||||
CurrentQuotedTweetUrl = string.Empty; // load from JS
|
||||
|
||||
timerProgress.Stop();
|
||||
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
||||
|
@@ -18,6 +18,9 @@ namespace TweetDck.Core.Handling{
|
||||
|
||||
private readonly FormBrowser form;
|
||||
|
||||
private string lastHighlightedTweet;
|
||||
private string lastHighlightedQuotedTweet;
|
||||
|
||||
public ContextMenuBrowser(FormBrowser form){
|
||||
this.form = form;
|
||||
}
|
||||
@@ -31,11 +34,14 @@ namespace TweetDck.Core.Handling{
|
||||
|
||||
base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
lastHighlightedTweet = TweetDeckBridge.LastHighlightedTweet;
|
||||
lastHighlightedQuotedTweet = TweetDeckBridge.LastHighlightedQuotedTweet;
|
||||
|
||||
if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
|
||||
model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedQuotedTweet)){
|
||||
if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){
|
||||
model.AddSeparator();
|
||||
model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
@@ -91,19 +97,19 @@ namespace TweetDck.Core.Handling{
|
||||
return true;
|
||||
|
||||
case MenuOpenTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(TweetDeckBridge.LastHighlightedTweet);
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedTweet);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweet, TextDataFormat.UnicodeText);
|
||||
Clipboard.SetText(lastHighlightedTweet, TextDataFormat.UnicodeText);
|
||||
return true;
|
||||
|
||||
case MenuOpenQuotedTweetUrl:
|
||||
BrowserUtils.OpenExternalBrowser(TweetDeckBridge.LastHighlightedQuotedTweet);
|
||||
BrowserUtils.OpenExternalBrowser(lastHighlightedQuotedTweet);
|
||||
return true;
|
||||
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.LastHighlightedQuotedTweet, TextDataFormat.UnicodeText);
|
||||
Clipboard.SetText(lastHighlightedQuotedTweet, TextDataFormat.UnicodeText);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ namespace TweetDck.Core.Handling{
|
||||
private const int MenuSkipTweet = 26600;
|
||||
private const int MenuFreeze = 26601;
|
||||
private const int MenuCopyTweetUrl = 26602;
|
||||
private const int MenuCopyTweetEmbeddedUrl = 26603;
|
||||
private const int MenuCopyQuotedTweetUrl = 26603;
|
||||
|
||||
private readonly FormNotification form;
|
||||
private readonly bool enableCustomMenu;
|
||||
@@ -29,8 +29,8 @@ namespace TweetDck.Core.Handling{
|
||||
if (!string.IsNullOrEmpty(form.CurrentUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address");
|
||||
|
||||
if (!string.IsNullOrEmpty(TweetDeckBridge.NotificationTweetEmbedded)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl, "Copy quoted tweet address");
|
||||
if (!string.IsNullOrEmpty(form.CurrentQuotedTweetUrl)){
|
||||
model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
|
||||
}
|
||||
|
||||
model.AddSeparator();
|
||||
@@ -61,8 +61,8 @@ namespace TweetDck.Core.Handling{
|
||||
Clipboard.SetText(form.CurrentUrl, TextDataFormat.UnicodeText);
|
||||
return true;
|
||||
|
||||
case MenuCopyTweetEmbeddedUrl:
|
||||
Clipboard.SetText(TweetDeckBridge.NotificationTweetEmbedded, TextDataFormat.UnicodeText);
|
||||
case MenuCopyQuotedTweetUrl:
|
||||
Clipboard.SetText(form.CurrentQuotedTweetUrl, TextDataFormat.UnicodeText);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,6 @@ namespace TweetDck.Core.Handling{
|
||||
public static string LastRightClickedLink = string.Empty;
|
||||
public static string LastHighlightedTweet = string.Empty;
|
||||
public static string LastHighlightedQuotedTweet = string.Empty;
|
||||
public static string NotificationTweetEmbedded = string.Empty;
|
||||
public static string ClipboardImagePath = string.Empty;
|
||||
|
||||
private readonly FormBrowser form;
|
||||
@@ -81,8 +80,8 @@ namespace TweetDck.Core.Handling{
|
||||
});
|
||||
}
|
||||
|
||||
public void SetNotificationTweetEmbedded(string link){
|
||||
form.InvokeSafe(() => NotificationTweetEmbedded = link);
|
||||
public void SetNotificationQuotedTweet(string link){
|
||||
notification.InvokeSafe(() => notification.CurrentQuotedTweetUrl = link);
|
||||
}
|
||||
|
||||
public void OpenSettingsMenu(){
|
||||
@@ -119,6 +118,10 @@ namespace TweetDck.Core.Handling{
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadNextNotification(){
|
||||
notification.InvokeSafe(notification.FinishCurrentTweet);
|
||||
}
|
||||
|
||||
public void TryPasteImage(){
|
||||
form.InvokeSafe(() => {
|
||||
if (Clipboard.ContainsImage()){
|
||||
|
@@ -20,7 +20,7 @@ namespace TweetDck.Core.Handling{
|
||||
|
||||
private static string CustomCSS{
|
||||
get{
|
||||
return @".scroll-styled-v::-webkit-scrollbar{width:8px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}a[data-full-url]{word-break:break-all}";
|
||||
return @".scroll-styled-v::-webkit-scrollbar{width:8px}.scroll-styled-v::-webkit-scrollbar-thumb{border-radius:0}a[data-full-url]{word-break:break-all}#td-skip{opacity:0;cursor:pointer;transition:opacity 0.15s ease}.td-hover #td-skip{opacity:0.75}#td-skip:hover{opacity:1}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace TweetDck.Core.Handling{
|
||||
|
||||
build.Append(@"</div></div></article>");
|
||||
|
||||
return new TweetNotification(build.ToString(), "", 95);
|
||||
return new TweetNotification(build.ToString(), "", 95, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,11 +84,15 @@ namespace TweetDck.Core.Handling{
|
||||
private readonly string html;
|
||||
private readonly string url;
|
||||
private readonly int characters;
|
||||
private readonly bool isExample;
|
||||
|
||||
public TweetNotification(string html, string url, int characters){
|
||||
public TweetNotification(string html, string url, int characters) : this(html, url, characters, false){}
|
||||
|
||||
private TweetNotification(string html, string url, int characters, bool isExample){
|
||||
this.html = html;
|
||||
this.url = url;
|
||||
this.characters = characters;
|
||||
this.isExample = isExample;
|
||||
}
|
||||
|
||||
public int GetDisplayDuration(int value){
|
||||
@@ -106,7 +110,7 @@ namespace TweetDck.Core.Handling{
|
||||
}
|
||||
|
||||
build.Append("</head>");
|
||||
build.Append("<body class='hearty'><div class='app-columns-container'><div class='column scroll-styled-v' style='width:100%;overflow-y:auto'>");
|
||||
build.Append("<body class='hearty'").Append(isExample ? " td-example-notification" : "").Append("><div class='app-columns-container'><div class='column scroll-styled-v' style='width:100%;overflow-y:auto'>");
|
||||
build.Append(html);
|
||||
build.Append("</div></div></body>");
|
||||
build.Append("</html>");
|
||||
|
30
Core/Other/FormAbout.Designer.cs
generated
30
Core/Other/FormAbout.Designer.cs
generated
@@ -28,7 +28,7 @@ namespace TweetDck.Core.Other {
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormAbout));
|
||||
this.pictureLogo = new System.Windows.Forms.PictureBox();
|
||||
this.labelDescription = new System.Windows.Forms.Label();
|
||||
this.labelSourceCode = new System.Windows.Forms.LinkLabel();
|
||||
this.labelTips = new System.Windows.Forms.LinkLabel();
|
||||
this.labelWebsite = new System.Windows.Forms.LinkLabel();
|
||||
this.tablePanelLinks = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.labelIssues = new System.Windows.Forms.LinkLabel();
|
||||
@@ -59,19 +59,19 @@ namespace TweetDck.Core.Other {
|
||||
this.labelDescription.Size = new System.Drawing.Size(232, 109);
|
||||
this.labelDescription.TabIndex = 1;
|
||||
//
|
||||
// labelSourceCode
|
||||
// labelTips
|
||||
//
|
||||
this.labelSourceCode.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.labelSourceCode.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelSourceCode.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
|
||||
this.labelSourceCode.Location = new System.Drawing.Point(117, 0);
|
||||
this.labelSourceCode.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.labelSourceCode.Name = "labelSourceCode";
|
||||
this.labelSourceCode.Size = new System.Drawing.Size(99, 16);
|
||||
this.labelSourceCode.TabIndex = 3;
|
||||
this.labelSourceCode.Text = "Source Code";
|
||||
this.labelSourceCode.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.labelSourceCode.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
|
||||
this.labelTips.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.labelTips.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelTips.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
|
||||
this.labelTips.Location = new System.Drawing.Point(117, 0);
|
||||
this.labelTips.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.labelTips.Name = "labelTips";
|
||||
this.labelTips.Size = new System.Drawing.Size(99, 16);
|
||||
this.labelTips.TabIndex = 3;
|
||||
this.labelTips.Text = "Tips && Tricks";
|
||||
this.labelTips.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
this.labelTips.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
|
||||
//
|
||||
// labelWebsite
|
||||
//
|
||||
@@ -98,7 +98,7 @@ namespace TweetDck.Core.Other {
|
||||
this.tablePanelLinks.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 35.14F));
|
||||
this.tablePanelLinks.Controls.Add(this.labelIssues, 2, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelWebsite, 0, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelSourceCode, 1, 0);
|
||||
this.tablePanelLinks.Controls.Add(this.labelTips, 1, 0);
|
||||
this.tablePanelLinks.Location = new System.Drawing.Point(12, 124);
|
||||
this.tablePanelLinks.Name = "tablePanelLinks";
|
||||
this.tablePanelLinks.RowCount = 1;
|
||||
@@ -146,7 +146,7 @@ namespace TweetDck.Core.Other {
|
||||
|
||||
private System.Windows.Forms.PictureBox pictureLogo;
|
||||
private System.Windows.Forms.Label labelDescription;
|
||||
private System.Windows.Forms.LinkLabel labelSourceCode;
|
||||
private System.Windows.Forms.LinkLabel labelTips;
|
||||
private System.Windows.Forms.LinkLabel labelWebsite;
|
||||
private System.Windows.Forms.TableLayoutPanel tablePanelLinks;
|
||||
private System.Windows.Forms.LinkLabel labelIssues;
|
||||
|
@@ -3,7 +3,7 @@ using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other{
|
||||
sealed partial class FormAbout : Form{
|
||||
private const string GitHubLink = "https://github.com/chylex/TweetDuck";
|
||||
private const string TipsLink = "https://github.com/chylex/TweetDuck/wiki";
|
||||
private const string IssuesLink = "https://github.com/chylex/TweetDuck/issues";
|
||||
|
||||
public FormAbout(){
|
||||
@@ -14,7 +14,7 @@ namespace TweetDck.Core.Other{
|
||||
labelDescription.Text = Program.BrandName+" was created by chylex as a replacement to the discontinued official TweetDeck client for Windows.\n\nThe program is available for free under the open source MIT license.";
|
||||
|
||||
labelWebsite.Links.Add(new LinkLabel.Link(0, labelWebsite.Text.Length, Program.Website));
|
||||
labelSourceCode.Links.Add(new LinkLabel.Link(0, labelSourceCode.Text.Length, GitHubLink));
|
||||
labelTips.Links.Add(new LinkLabel.Link(0, labelTips.Text.Length, TipsLink));
|
||||
labelIssues.Links.Add(new LinkLabel.Link(0, labelIssues.Text.Length, IssuesLink));
|
||||
}
|
||||
|
||||
|
@@ -34,6 +34,10 @@ namespace TweetDck.Core.Other{
|
||||
icon = SystemIcons.Error;
|
||||
break;
|
||||
|
||||
case MessageBoxIcon.Question:
|
||||
icon = SystemIcons.Question;
|
||||
break;
|
||||
|
||||
default:
|
||||
icon = null;
|
||||
labelMessage.Location = new Point(labelMessage.Location.X-32, labelMessage.Location.Y);
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Configuration;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings{
|
||||
@@ -18,8 +17,7 @@ namespace TweetDck.Core.Other.Settings{
|
||||
|
||||
protected static void PromptRestart(){
|
||||
if (MessageBox.Show("The application must restart for the setting to take place. Do you want to restart now?", Program.BrandName+" Settings", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes){
|
||||
Process.Start(Application.ExecutablePath, "-restart");
|
||||
Application.Exit();
|
||||
Program.Restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,14 +48,14 @@
|
||||
this.textBoxBrowserCSS.Multiline = true;
|
||||
this.textBoxBrowserCSS.Name = "textBoxBrowserCSS";
|
||||
this.textBoxBrowserCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.textBoxBrowserCSS.Size = new System.Drawing.Size(226, 193);
|
||||
this.textBoxBrowserCSS.Size = new System.Drawing.Size(373, 253);
|
||||
this.textBoxBrowserCSS.TabIndex = 0;
|
||||
this.textBoxBrowserCSS.WordWrap = false;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnCancel.Location = new System.Drawing.Point(354, 227);
|
||||
this.btnCancel.Location = new System.Drawing.Point(654, 287);
|
||||
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);
|
||||
@@ -67,7 +67,7 @@
|
||||
// btnApply
|
||||
//
|
||||
this.btnApply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.btnApply.Location = new System.Drawing.Point(416, 227);
|
||||
this.btnApply.Location = new System.Drawing.Point(716, 287);
|
||||
this.btnApply.Name = "btnApply";
|
||||
this.btnApply.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnApply.Size = new System.Drawing.Size(56, 23);
|
||||
@@ -95,8 +95,8 @@
|
||||
this.splitContainer.Panel2.Controls.Add(this.labelNotification);
|
||||
this.splitContainer.Panel2.Controls.Add(this.textBoxNotificationCSS);
|
||||
this.splitContainer.Panel2MinSize = 64;
|
||||
this.splitContainer.Size = new System.Drawing.Size(460, 209);
|
||||
this.splitContainer.SplitterDistance = 226;
|
||||
this.splitContainer.Size = new System.Drawing.Size(760, 269);
|
||||
this.splitContainer.SplitterDistance = 373;
|
||||
this.splitContainer.SplitterWidth = 5;
|
||||
this.splitContainer.TabIndex = 5;
|
||||
//
|
||||
@@ -131,7 +131,7 @@
|
||||
this.textBoxNotificationCSS.Multiline = true;
|
||||
this.textBoxNotificationCSS.Name = "textBoxNotificationCSS";
|
||||
this.textBoxNotificationCSS.ScrollBars = System.Windows.Forms.ScrollBars.Both;
|
||||
this.textBoxNotificationCSS.Size = new System.Drawing.Size(226, 193);
|
||||
this.textBoxNotificationCSS.Size = new System.Drawing.Size(378, 253);
|
||||
this.textBoxNotificationCSS.TabIndex = 1;
|
||||
this.textBoxNotificationCSS.WordWrap = false;
|
||||
//
|
||||
@@ -139,7 +139,7 @@
|
||||
//
|
||||
this.labelWarning.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.labelWarning.AutoSize = true;
|
||||
this.labelWarning.Location = new System.Drawing.Point(9, 232);
|
||||
this.labelWarning.Location = new System.Drawing.Point(9, 292);
|
||||
this.labelWarning.Name = "labelWarning";
|
||||
this.labelWarning.Size = new System.Drawing.Size(341, 13);
|
||||
this.labelWarning.TabIndex = 6;
|
||||
@@ -149,7 +149,7 @@
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(484, 262);
|
||||
this.ClientSize = new System.Drawing.Size(784, 322);
|
||||
this.Controls.Add(this.labelWarning);
|
||||
this.Controls.Add(this.splitContainer);
|
||||
this.Controls.Add(this.btnApply);
|
||||
|
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
sealed partial class DialogSettingsCSS : Form{
|
||||
@@ -20,7 +21,10 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
|
||||
Text = Program.BrandName+" Settings - CSS";
|
||||
|
||||
textBoxBrowserCSS.EnableMultilineShortcuts();
|
||||
textBoxBrowserCSS.Text = Program.UserConfig.CustomBrowserCSS ?? "";
|
||||
|
||||
textBoxNotificationCSS.EnableMultilineShortcuts();
|
||||
textBoxNotificationCSS.Text = Program.UserConfig.CustomNotificationCSS ?? "";
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Utils;
|
||||
|
||||
namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
@@ -16,6 +16,7 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
|
||||
Text = Program.BrandName+" Settings - CEF Arguments";
|
||||
|
||||
textBoxArgs.EnableMultilineShortcuts();
|
||||
textBoxArgs.Text = Program.UserConfig.CustomCefArgs ?? "";
|
||||
textBoxArgs.Select(textBoxArgs.Text.Length, 0);
|
||||
}
|
||||
@@ -33,7 +34,7 @@ namespace TweetDck.Core.Other.Settings.Dialogs{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = CommandLineArgsParser.AddToDictionary(CefArgs, new Dictionary<string, string>());
|
||||
int count = CommandLineArgsParser.ReadCefArguments(CefArgs).Count;
|
||||
string prompt = count == 0 && !string.IsNullOrWhiteSpace(prevArgs) ? "All arguments will be removed from the settings. Continue?" : count+(count == 1 ? " argument" : " arguments")+" will be added to the settings. Continue?";
|
||||
|
||||
if (MessageBox.Show(prompt, "Confirm CEF Arguments", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.OK){
|
||||
|
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
@@ -105,8 +104,7 @@ namespace TweetDck.Core.Other.Settings.Export{
|
||||
|
||||
// okay to and restart, 'cookies' is always the last entry
|
||||
IsRestarting = true;
|
||||
Process.Start(Application.ExecutablePath, "-restart -importcookies");
|
||||
Application.Exit();
|
||||
Program.Restart(new string[]{ "-importcookies" });
|
||||
}
|
||||
|
||||
break;
|
||||
|
58
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
58
Core/Other/Settings/TabSettingsAdvanced.Designer.cs
generated
@@ -29,13 +29,18 @@
|
||||
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
|
||||
this.btnEditCefArgs = new System.Windows.Forms.Button();
|
||||
this.btnEditCSS = new System.Windows.Forms.Button();
|
||||
this.btnRestartLog = new System.Windows.Forms.Button();
|
||||
this.btnRestart = new System.Windows.Forms.Button();
|
||||
this.btnReset = new System.Windows.Forms.Button();
|
||||
this.btnImport = new System.Windows.Forms.Button();
|
||||
this.btnExport = new System.Windows.Forms.Button();
|
||||
this.groupPerformance = new System.Windows.Forms.GroupBox();
|
||||
this.groupConfiguration = new System.Windows.Forms.GroupBox();
|
||||
this.groupApp = new System.Windows.Forms.GroupBox();
|
||||
this.btnOpenAppFolder = new System.Windows.Forms.Button();
|
||||
this.groupPerformance.SuspendLayout();
|
||||
this.groupConfiguration.SuspendLayout();
|
||||
this.groupApp.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnClearCache
|
||||
@@ -86,6 +91,30 @@
|
||||
this.btnEditCSS.UseVisualStyleBackColor = true;
|
||||
this.btnEditCSS.Click += new System.EventHandler(this.btnEditCSS_Click);
|
||||
//
|
||||
// btnRestartLog
|
||||
//
|
||||
this.btnRestartLog.Location = new System.Drawing.Point(6, 77);
|
||||
this.btnRestartLog.Name = "btnRestartLog";
|
||||
this.btnRestartLog.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnRestartLog.TabIndex = 18;
|
||||
this.btnRestartLog.Text = "Restart with Logging";
|
||||
this.toolTip.SetToolTip(this.btnRestartLog, "Restarts the program and enables logging\r\ninto a debug.txt file in the installati" +
|
||||
"on folder.");
|
||||
this.btnRestartLog.UseVisualStyleBackColor = true;
|
||||
this.btnRestartLog.Click += new System.EventHandler(this.btnRestartLog_Click);
|
||||
//
|
||||
// btnRestart
|
||||
//
|
||||
this.btnRestart.Location = new System.Drawing.Point(6, 48);
|
||||
this.btnRestart.Name = "btnRestart";
|
||||
this.btnRestart.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnRestart.TabIndex = 17;
|
||||
this.btnRestart.Text = "Restart the Program";
|
||||
this.toolTip.SetToolTip(this.btnRestart, "Restarts the program using the same command\r\nline arguments that were used at lau" +
|
||||
"nch.");
|
||||
this.btnRestart.UseVisualStyleBackColor = true;
|
||||
this.btnRestart.Click += new System.EventHandler(this.btnRestart_Click);
|
||||
//
|
||||
// btnReset
|
||||
//
|
||||
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
@@ -148,10 +177,34 @@
|
||||
this.groupConfiguration.TabStop = false;
|
||||
this.groupConfiguration.Text = "Configuration";
|
||||
//
|
||||
// groupApp
|
||||
//
|
||||
this.groupApp.Controls.Add(this.btnOpenAppFolder);
|
||||
this.groupApp.Controls.Add(this.btnRestartLog);
|
||||
this.groupApp.Controls.Add(this.btnRestart);
|
||||
this.groupApp.Location = new System.Drawing.Point(198, 9);
|
||||
this.groupApp.Name = "groupApp";
|
||||
this.groupApp.Size = new System.Drawing.Size(183, 106);
|
||||
this.groupApp.TabIndex = 20;
|
||||
this.groupApp.TabStop = false;
|
||||
this.groupApp.Text = "App";
|
||||
//
|
||||
// btnOpenAppFolder
|
||||
//
|
||||
this.btnOpenAppFolder.Location = new System.Drawing.Point(6, 19);
|
||||
this.btnOpenAppFolder.Name = "btnOpenAppFolder";
|
||||
this.btnOpenAppFolder.Size = new System.Drawing.Size(171, 23);
|
||||
this.btnOpenAppFolder.TabIndex = 16;
|
||||
this.btnOpenAppFolder.Text = "Open Program Folder";
|
||||
this.toolTip.SetToolTip(this.btnOpenAppFolder, "Opens the folder where the app is located.");
|
||||
this.btnOpenAppFolder.UseVisualStyleBackColor = true;
|
||||
this.btnOpenAppFolder.Click += new System.EventHandler(this.btnOpenAppFolder_Click);
|
||||
//
|
||||
// TabSettingsAdvanced
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.groupApp);
|
||||
this.Controls.Add(this.groupConfiguration);
|
||||
this.Controls.Add(this.groupPerformance);
|
||||
this.Controls.Add(this.btnReset);
|
||||
@@ -162,6 +215,7 @@
|
||||
this.groupPerformance.ResumeLayout(false);
|
||||
this.groupPerformance.PerformLayout();
|
||||
this.groupConfiguration.ResumeLayout(false);
|
||||
this.groupApp.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -179,5 +233,9 @@
|
||||
private System.Windows.Forms.GroupBox groupConfiguration;
|
||||
private System.Windows.Forms.Button btnEditCefArgs;
|
||||
private System.Windows.Forms.Button btnEditCSS;
|
||||
private System.Windows.Forms.GroupBox groupApp;
|
||||
private System.Windows.Forms.Button btnRestartLog;
|
||||
private System.Windows.Forms.Button btnRestart;
|
||||
private System.Windows.Forms.Button btnOpenAppFolder;
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using TweetDck.Core.Controls;
|
||||
using TweetDck.Core.Other.Settings.Dialogs;
|
||||
@@ -70,13 +71,16 @@ namespace TweetDck.Core.Other.Settings{
|
||||
|
||||
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
||||
Config.CustomCefArgs = form.CefArgs;
|
||||
form.Dispose();
|
||||
PromptRestart();
|
||||
}
|
||||
else{
|
||||
form.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnEditCSS_Click(object sender, EventArgs e){
|
||||
DialogSettingsCSS form = new DialogSettingsCSS();
|
||||
|
||||
using(DialogSettingsCSS form = new DialogSettingsCSS()){
|
||||
if (form.ShowDialog(ParentForm) == DialogResult.OK){
|
||||
bool hasChangedBrowser = form.BrowserCSS != Config.CustomBrowserCSS;
|
||||
|
||||
@@ -88,6 +92,7 @@ namespace TweetDck.Core.Other.Settings{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void btnExport_Click(object sender, EventArgs e){
|
||||
DialogResult resultSaveCredentials = MessageBox.Show("Do you want to include your login session? This will not save your password into the file, but it will allow anyone with the file to login into TweetDeck as you.", "Export "+Program.BrandName+" Settings", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
|
||||
@@ -151,5 +156,17 @@ namespace TweetDck.Core.Other.Settings{
|
||||
((FormSettings)ParentForm).ReloadUI();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnOpenAppFolder_Click(object sender, EventArgs e){
|
||||
using(Process.Start("explorer.exe", "\""+Program.ProgramPath+"\"")){}
|
||||
}
|
||||
|
||||
private void btnRestart_Click(object sender, EventArgs e){
|
||||
Program.Restart();
|
||||
}
|
||||
|
||||
private void btnRestartLog_Click(object sender, EventArgs e){
|
||||
Program.Restart(new string[]{ "-log" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
107
Core/Utils/CommandLineArgs.cs
Normal file
107
Core/Utils/CommandLineArgs.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
class CommandLineArgs{
|
||||
public static CommandLineArgs FromStringArray(char entryChar, string[] array){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
ReadStringArray(entryChar, array, args);
|
||||
return args;
|
||||
}
|
||||
|
||||
public static void ReadStringArray(char entryChar, string[] array, CommandLineArgs targetArgs){
|
||||
for(int index = 0; index < array.Length; index++){
|
||||
string entry = array[index];
|
||||
|
||||
if (entry.Length > 0 && entry[0] == entryChar){
|
||||
if (index < array.Length-1){
|
||||
string potentialValue = array[index+1];
|
||||
|
||||
if (potentialValue.Length > 0 && potentialValue[0] == entryChar){
|
||||
targetArgs.AddFlag(entry);
|
||||
}
|
||||
else{
|
||||
targetArgs.SetValue(entry, potentialValue);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
else{
|
||||
targetArgs.AddFlag(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly HashSet<string> flags = new HashSet<string>();
|
||||
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
|
||||
|
||||
public int Count{
|
||||
get{
|
||||
return flags.Count+values.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFlag(string flag){
|
||||
flags.Add(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public bool HasFlag(string flag){
|
||||
return flags.Contains(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public void RemoveFlag(string flag){
|
||||
flags.Remove(flag.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public void SetValue(string key, string value){
|
||||
values[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
public string GetValue(string key, string defaultValue){
|
||||
string val;
|
||||
return values.TryGetValue(key.ToLowerInvariant(), out val) ? val : defaultValue;
|
||||
}
|
||||
|
||||
public void RemoveValue(string key){
|
||||
values.Remove(key.ToLowerInvariant());
|
||||
}
|
||||
|
||||
public CommandLineArgs Clone(){
|
||||
CommandLineArgs copy = new CommandLineArgs();
|
||||
|
||||
foreach(string flag in flags){
|
||||
copy.AddFlag(flag);
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
copy.SetValue(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void ToDictionary(IDictionary<string, string> target){
|
||||
foreach(string flag in flags){
|
||||
target[flag] = "1";
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
target[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString(){
|
||||
StringBuilder build = new StringBuilder();
|
||||
|
||||
foreach(string flag in flags){
|
||||
build.Append(flag).Append(' ');
|
||||
}
|
||||
|
||||
foreach(KeyValuePair<string, string> kvp in values){
|
||||
build.Append(kvp.Key).Append(" \"").Append(kvp.Value).Append("\" ");
|
||||
}
|
||||
|
||||
return build.Remove(build.Length-1, 1).ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace TweetDck.Core.Utils{
|
||||
static class CommandLineArgsParser{
|
||||
@@ -7,18 +6,18 @@ namespace TweetDck.Core.Utils{
|
||||
|
||||
private static Regex SplitRegex{
|
||||
get{
|
||||
return splitRegex ?? (splitRegex = new Regex(@"([^=\s]+(?:=(?:""[^""]*?""|[^ ]*))?)", RegexOptions.Compiled));
|
||||
return splitRegex ?? (splitRegex = new Regex(@"([^=\s]+(?:=(?:[^ ]*""[^""]*?""[^ ]*|[^ ]*))?)", RegexOptions.Compiled));
|
||||
}
|
||||
}
|
||||
|
||||
public static int AddToDictionary(string args, IDictionary<string, string> dictionary){
|
||||
if (string.IsNullOrWhiteSpace(args)){
|
||||
return 0;
|
||||
public static CommandLineArgs ReadCefArguments(string argumentString){
|
||||
CommandLineArgs args = new CommandLineArgs();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(argumentString)){
|
||||
return args;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
foreach(Match match in SplitRegex.Matches(args)){
|
||||
foreach(Match match in SplitRegex.Matches(argumentString)){
|
||||
string matchValue = match.Value;
|
||||
|
||||
int indexEquals = matchValue.IndexOf('=');
|
||||
@@ -34,12 +33,11 @@ namespace TweetDck.Core.Utils{
|
||||
}
|
||||
|
||||
if (key.Length != 0){
|
||||
dictionary[key] = value;
|
||||
++count;
|
||||
args.SetValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ namespace TweetDck.Core.Utils{
|
||||
public const int MOUSEEVENTF_RIGHTUP = 0x10;
|
||||
|
||||
public const int SB_HORZ = 0;
|
||||
public const int BCM_SETSHIELD = 0x160C;
|
||||
|
||||
public const int WH_MOUSE_LL = 14;
|
||||
public const int WH_MOUSEWHEEL = 0x020A;
|
||||
|
148
Migration/FormMigrationQuestion.Designer.cs
generated
148
Migration/FormMigrationQuestion.Designer.cs
generated
@@ -1,148 +0,0 @@
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Migration {
|
||||
partial class FormMigrationQuestion {
|
||||
/// <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.btnIgnore = new System.Windows.Forms.Button();
|
||||
this.panelButtons = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnAskLater = new System.Windows.Forms.Button();
|
||||
this.btnMigrate = new System.Windows.Forms.Button();
|
||||
this.btnMigrateUninstall = new System.Windows.Forms.Button();
|
||||
this.labelQuestion = new System.Windows.Forms.Label();
|
||||
this.panelButtons.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// btnIgnore
|
||||
//
|
||||
this.btnIgnore.AutoSize = true;
|
||||
this.btnIgnore.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnIgnore.Location = new System.Drawing.Point(391, 0);
|
||||
this.btnIgnore.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnIgnore.Name = "btnIgnore";
|
||||
this.btnIgnore.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnIgnore.Size = new System.Drawing.Size(53, 23);
|
||||
this.btnIgnore.TabIndex = 1;
|
||||
this.btnIgnore.Text = "Ignore";
|
||||
this.btnIgnore.UseVisualStyleBackColor = true;
|
||||
this.btnIgnore.Click += new System.EventHandler(this.btnIgnore_Click);
|
||||
//
|
||||
// panelButtons
|
||||
//
|
||||
this.panelButtons.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.panelButtons.Controls.Add(this.btnAskLater);
|
||||
this.panelButtons.Controls.Add(this.btnIgnore);
|
||||
this.panelButtons.Controls.Add(this.btnMigrate);
|
||||
this.panelButtons.Controls.Add(this.btnMigrateUninstall);
|
||||
this.panelButtons.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.panelButtons.Location = new System.Drawing.Point(12, 67);
|
||||
this.panelButtons.Name = "panelButtons";
|
||||
this.panelButtons.Size = new System.Drawing.Size(518, 23);
|
||||
this.panelButtons.TabIndex = 0;
|
||||
//
|
||||
// btnAskLater
|
||||
//
|
||||
this.btnAskLater.AutoSize = true;
|
||||
this.btnAskLater.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnAskLater.Location = new System.Drawing.Point(450, 0);
|
||||
this.btnAskLater.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
|
||||
this.btnAskLater.Name = "btnAskLater";
|
||||
this.btnAskLater.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnAskLater.Size = new System.Drawing.Size(68, 23);
|
||||
this.btnAskLater.TabIndex = 0;
|
||||
this.btnAskLater.Text = "Ask Later";
|
||||
this.btnAskLater.UseVisualStyleBackColor = true;
|
||||
this.btnAskLater.Click += new System.EventHandler(this.btnAskLater_Click);
|
||||
//
|
||||
// btnMigrate
|
||||
//
|
||||
this.btnMigrate.AutoSize = true;
|
||||
this.btnMigrate.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnMigrate.Location = new System.Drawing.Point(327, 0);
|
||||
this.btnMigrate.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrate.Name = "btnMigrate";
|
||||
this.btnMigrate.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrate.Size = new System.Drawing.Size(58, 23);
|
||||
this.btnMigrate.TabIndex = 3;
|
||||
this.btnMigrate.Text = "Migrate";
|
||||
this.btnMigrate.UseVisualStyleBackColor = true;
|
||||
this.btnMigrate.Click += new System.EventHandler(this.btnMigrate_Click);
|
||||
//
|
||||
// btnMigrateUninstall
|
||||
//
|
||||
this.btnMigrateUninstall.AutoSize = true;
|
||||
this.btnMigrateUninstall.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
||||
this.btnMigrateUninstall.Location = new System.Drawing.Point(223, 0);
|
||||
this.btnMigrateUninstall.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
|
||||
this.btnMigrateUninstall.Name = "btnMigrateUninstall";
|
||||
this.btnMigrateUninstall.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
|
||||
this.btnMigrateUninstall.Size = new System.Drawing.Size(98, 23);
|
||||
this.btnMigrateUninstall.TabIndex = 4;
|
||||
this.btnMigrateUninstall.Text = "Migrate && Purge";
|
||||
this.btnMigrateUninstall.UseVisualStyleBackColor = true;
|
||||
this.btnMigrateUninstall.Click += new System.EventHandler(this.btnMigrateUninstall_Click);
|
||||
//
|
||||
// labelQuestion
|
||||
//
|
||||
this.labelQuestion.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.labelQuestion.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
|
||||
this.labelQuestion.Location = new System.Drawing.Point(49, 9);
|
||||
this.labelQuestion.Margin = new System.Windows.Forms.Padding(40, 3, 3, 3);
|
||||
this.labelQuestion.Name = "labelQuestion";
|
||||
this.labelQuestion.Size = new System.Drawing.Size(481, 52);
|
||||
this.labelQuestion.TabIndex = 2;
|
||||
//
|
||||
// FormMigrationQuestion
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(542, 102);
|
||||
this.Controls.Add(this.labelQuestion);
|
||||
this.Controls.Add(this.panelButtons);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.MaximizeBox = false;
|
||||
this.MinimizeBox = false;
|
||||
this.Name = "FormMigrationQuestion";
|
||||
this.ShowIcon = false;
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "TweetDeck Migration";
|
||||
this.panelButtons.ResumeLayout(false);
|
||||
this.panelButtons.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button btnIgnore;
|
||||
private System.Windows.Forms.FlowLayoutPanel panelButtons;
|
||||
private System.Windows.Forms.Button btnMigrate;
|
||||
private System.Windows.Forms.Label labelQuestion;
|
||||
private System.Windows.Forms.Button btnAskLater;
|
||||
private System.Windows.Forms.Button btnMigrateUninstall;
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace TweetDck.Migration{
|
||||
partial class FormMigrationQuestion : Form{
|
||||
public MigrationDecision Decision { get; private set; }
|
||||
|
||||
public FormMigrationQuestion(){
|
||||
InitializeComponent();
|
||||
|
||||
labelQuestion.Text = "Hey there, I found some TweetDeck data! Do you want to »Migrate« it and delete the old data folder, »Ignore« the request forever, or try "+Program.BrandName+" out first?\r\nYou may also »Migrate && Purge« which uninstalls TweetDeck too!";
|
||||
}
|
||||
|
||||
protected override void OnPaint(PaintEventArgs e){
|
||||
e.Graphics.DrawIcon(SystemIcons.Question, 10, 10);
|
||||
base.OnPaint(e);
|
||||
}
|
||||
|
||||
private void btnMigrateUninstall_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.MigratePurge);
|
||||
}
|
||||
|
||||
private void btnMigrate_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.Migrate);
|
||||
}
|
||||
|
||||
private void btnIgnore_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.Ignore);
|
||||
}
|
||||
|
||||
private void btnAskLater_Click(object sender, EventArgs e){
|
||||
Close(MigrationDecision.AskLater);
|
||||
}
|
||||
|
||||
private void Close(MigrationDecision decision){
|
||||
Decision = decision;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,9 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
using TweetDck.Migration.Helpers;
|
||||
using TweetDck.Core.Other;
|
||||
using System.Drawing;
|
||||
using TweetDck.Core.Controls;
|
||||
|
||||
namespace TweetDck.Migration{
|
||||
static class MigrationManager{
|
||||
@@ -32,8 +34,26 @@ namespace TweetDck.Migration{
|
||||
if (!Program.UserConfig.IgnoreMigration && Directory.Exists(TweetDeckPath)){
|
||||
MigrationDecision decision;
|
||||
|
||||
using(FormMigrationQuestion formQuestion = new FormMigrationQuestion()){
|
||||
decision = formQuestion.ShowDialog() == DialogResult.OK ? formQuestion.Decision : MigrationDecision.AskLater;
|
||||
const string prompt = "Hey there, I found some TweetDeck data! Do you want to »Migrate« it and delete the old data folder, »Ignore« the prompt, or try "+Program.BrandName+" out first? You may also »Migrate && Purge« which uninstalls TweetDeck too!";
|
||||
|
||||
using(FormMessage formQuestion = new FormMessage("TweetDeck Migration", prompt, MessageBoxIcon.Question)){
|
||||
formQuestion.AddButton("Ask Later");
|
||||
Button btnIgnore = formQuestion.AddButton("Ignore");
|
||||
Button btnMigrate = formQuestion.AddButton("Migrate");
|
||||
Button btnMigrateAndPurge = formQuestion.AddButton("Migrate && Purge");
|
||||
|
||||
btnMigrateAndPurge.Location = new Point(btnMigrateAndPurge.Location.X-20, btnMigrateAndPurge.Location.Y);
|
||||
btnMigrateAndPurge.Width += 20;
|
||||
btnMigrateAndPurge.SetElevated();
|
||||
|
||||
if (formQuestion.ShowDialog() == DialogResult.OK){
|
||||
decision = formQuestion.ClickedButton == btnMigrateAndPurge ? MigrationDecision.MigratePurge :
|
||||
formQuestion.ClickedButton == btnMigrate ? MigrationDecision.Migrate :
|
||||
formQuestion.ClickedButton == btnIgnore ? MigrationDecision.Ignore : MigrationDecision.AskLater;
|
||||
}
|
||||
else{
|
||||
decision = MigrationDecision.AskLater;
|
||||
}
|
||||
}
|
||||
|
||||
switch(decision){
|
||||
@@ -66,12 +86,22 @@ namespace TweetDck.Migration{
|
||||
}
|
||||
}
|
||||
else if (!Program.UserConfig.IgnoreUninstallCheck){
|
||||
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
|
||||
string guid = MigrationUtils.FindProgramGuidByDisplayName("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);
|
||||
if (guid != null){
|
||||
const string prompt = "TweetDeck is still installed on your computer, do you want to uninstall it?";
|
||||
|
||||
using(FormMessage formQuestion = new FormMessage("Uninstall TweetDeck", prompt, MessageBoxIcon.Question)){
|
||||
formQuestion.AddButton("No");
|
||||
|
||||
Button btnYes = formQuestion.AddButton("Yes");
|
||||
btnYes.SetElevated();
|
||||
|
||||
if (formQuestion.ShowDialog() == DialogResult.OK && formQuestion.ClickedButton == btnYes && MigrationUtils.RunUninstaller(guid, 0)){
|
||||
CleanupTweetDeck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Program.UserConfig.IgnoreUninstallCheck = true;
|
||||
Program.UserConfig.Save();
|
||||
@@ -135,10 +165,10 @@ namespace TweetDck.Migration{
|
||||
|
||||
if (decision == MigrationDecision.MigratePurge){
|
||||
// uninstall in the background
|
||||
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
|
||||
string guid = MigrationUtils.FindProgramGuidByDisplayName("TweetDeck");
|
||||
|
||||
if (guid != null){
|
||||
RunUninstaller(guid, 5000);
|
||||
if (guid != null && !MigrationUtils.RunUninstaller(guid, 5000)){
|
||||
return;
|
||||
}
|
||||
|
||||
// registry cleanup
|
||||
@@ -162,18 +192,6 @@ namespace TweetDck.Migration{
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@@ -1,10 +1,33 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace TweetDck.Migration.Helpers{
|
||||
static class ProgramRegistrySearch{
|
||||
public static string FindByDisplayName(string displayName){
|
||||
namespace TweetDck.Migration{
|
||||
static class MigrationUtils{
|
||||
public static bool RunUninstaller(string guid, int timeout){
|
||||
try{
|
||||
Process uninstaller = Process.Start(new ProcessStartInfo{
|
||||
FileName = "msiexec.exe",
|
||||
Arguments = "/x "+guid+" /quiet /qn",
|
||||
Verb = "runas"
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
return true;
|
||||
}catch(Exception){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string FindProgramGuidByDisplayName(string displayName){
|
||||
Predicate<RegistryKey> predicate = key => displayName.Equals(key.GetValue("DisplayName") as string, StringComparison.OrdinalIgnoreCase);
|
||||
string guid;
|
||||
|
@@ -24,12 +24,6 @@ namespace TweetDck.Plugins{
|
||||
}
|
||||
}
|
||||
|
||||
public string FolderPath{
|
||||
get{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasConfig{
|
||||
get{
|
||||
return ConfigFile.Length > 0 && GetFullPathIfSafe(ConfigFile).Length > 0;
|
||||
|
@@ -40,7 +40,7 @@ namespace TweetDck.Plugins{
|
||||
public string ReadFile(int token, string path, bool cache){
|
||||
string fullPath = GetFullPathIfSafe(token, path);
|
||||
|
||||
if (fullPath == string.Empty){
|
||||
if (fullPath.Length == 0){
|
||||
throw new Exception("File path has to be relative to the plugin folder.");
|
||||
}
|
||||
|
||||
|
60
Program.cs
60
Program.cs
@@ -19,16 +19,17 @@ using System.Security.AccessControl;
|
||||
namespace TweetDck{
|
||||
static class Program{
|
||||
public const string BrandName = "TweetDuck";
|
||||
public const string Website = "http://tweetduck.chylex.com";
|
||||
public const string Website = "https://tweetduck.chylex.com";
|
||||
|
||||
public const string BrowserSubprocess = BrandName+".Browser.exe";
|
||||
|
||||
public const string VersionTag = "1.3.3";
|
||||
public const string VersionFull = "1.3.3.0";
|
||||
public const string VersionTag = "1.4.3";
|
||||
public const string VersionFull = "1.4.3.0";
|
||||
|
||||
public static readonly Version Version = new Version(VersionTag);
|
||||
|
||||
public static readonly bool IsPortable = File.Exists("makeportable");
|
||||
private static readonly CommandLineArgs Args = CommandLineArgs.FromStringArray('-', Environment.GetCommandLineArgs());
|
||||
|
||||
public static readonly string ProgramPath = AppDomain.CurrentDomain.BaseDirectory;
|
||||
public static readonly string StoragePath = IsPortable ? Path.Combine(ProgramPath, "portable", "storage") : GetDataStoragePath();
|
||||
@@ -70,9 +71,7 @@ namespace TweetDck{
|
||||
}
|
||||
};
|
||||
|
||||
string[] programArguments = Environment.GetCommandLineArgs();
|
||||
|
||||
if (programArguments.Contains("-restart")){
|
||||
if (Args.HasFlag("-restart")){
|
||||
for(int attempt = 0; attempt < 41; attempt++){
|
||||
if (LockManager.Lock()){
|
||||
break;
|
||||
@@ -109,7 +108,7 @@ namespace TweetDck{
|
||||
}
|
||||
}
|
||||
|
||||
if (programArguments.Contains("-importcookies")){
|
||||
if (Args.HasFlag("-importcookies")){
|
||||
ExportManager.ImportCookies();
|
||||
}
|
||||
|
||||
@@ -118,15 +117,21 @@ namespace TweetDck{
|
||||
CefSettings settings = new CefSettings{
|
||||
AcceptLanguageList = BrowserUtils.HeaderAcceptLanguage,
|
||||
UserAgent = BrowserUtils.HeaderUserAgent,
|
||||
Locale = GetCmdArgumentValue("locale") ?? "en",
|
||||
Locale = Args.GetValue("-locale", "en"),
|
||||
CachePath = StoragePath,
|
||||
BrowserSubprocessPath = File.Exists(BrowserSubprocess) ? BrowserSubprocess : "CefSharp.BrowserSubprocess.exe",
|
||||
LogFile = Path.Combine(StoragePath, "TD_Console.txt"),
|
||||
#if !DEBUG
|
||||
LogSeverity = programArguments.Contains("-log") ? LogSeverity.Info : LogSeverity.Disable
|
||||
LogSeverity = Args.HasFlag("-log") ? LogSeverity.Info : LogSeverity.Disable
|
||||
#endif
|
||||
};
|
||||
|
||||
CommandLineArgsParser.AddToDictionary(UserConfig.CustomCefArgs, settings.CefCommandLineArgs);
|
||||
CommandLineArgsParser.ReadCefArguments(UserConfig.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
|
||||
if (!HardwareAcceleration.IsEnabled){
|
||||
settings.CefCommandLineArgs["disable-gpu"] = "1";
|
||||
settings.CefCommandLineArgs["disable-gpu-vsync"] = "1";
|
||||
}
|
||||
|
||||
Cef.Initialize(settings, false, new BrowserProcessHandler());
|
||||
|
||||
@@ -143,7 +148,7 @@ namespace TweetDck{
|
||||
if (mainForm.UpdateInstallerPath != null){
|
||||
ExitCleanup();
|
||||
|
||||
Process.Start(mainForm.UpdateInstallerPath, "/SP- /SILENT /NOICONS /CLOSEAPPLICATIONS");
|
||||
Process.Start(mainForm.UpdateInstallerPath, "/SP- /SILENT /CLOSEAPPLICATIONS /UPDATEPATH=\""+ProgramPath+"\""+(IsPortable ? " /PORTABLE=1" : "")); // ProgramPath has a trailing backslash
|
||||
Application.Exit();
|
||||
}
|
||||
}
|
||||
@@ -154,18 +159,18 @@ namespace TweetDck{
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCmdArgumentValue(string search){
|
||||
string[] args = Environment.GetCommandLineArgs();
|
||||
int index = Array.FindIndex(args, arg => arg.Equals(search, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return index >= 0 && index < args.Length-1 ? args[index+1] : null;
|
||||
}
|
||||
|
||||
private static string GetDataStoragePath(){
|
||||
string custom = GetCmdArgumentValue("-datafolder");
|
||||
string custom = Args.GetValue("-datafolder", null);
|
||||
|
||||
if (custom != null && (custom.Contains(Path.DirectorySeparatorChar) || custom.Contains(Path.AltDirectorySeparatorChar))){
|
||||
return custom;
|
||||
if (Path.GetInvalidPathChars().Any(custom.Contains)){
|
||||
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder contains invalid characters:\n"+custom);
|
||||
}
|
||||
else if (!Path.IsPathRooted(custom)){
|
||||
Reporter.HandleEarlyFailure("Data Folder Invalid", "The data folder has to be either a simple folder name, or a full path:\n"+custom);
|
||||
}
|
||||
|
||||
return Environment.ExpandEnvironmentVariables(custom);
|
||||
}
|
||||
else{
|
||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), custom ?? BrandName);
|
||||
@@ -188,6 +193,21 @@ namespace TweetDck{
|
||||
ReloadConfig();
|
||||
}
|
||||
|
||||
public static void Restart(){
|
||||
Restart(new string[0]);
|
||||
}
|
||||
|
||||
public static void Restart(string[] extraArgs){
|
||||
CommandLineArgs args = Args.Clone();
|
||||
args.AddFlag("-restart");
|
||||
args.RemoveFlag("-importcookies");
|
||||
|
||||
CommandLineArgs.ReadStringArray('-', extraArgs, args);
|
||||
|
||||
Process.Start(Application.ExecutablePath, args.ToString());
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private static void ExitCleanup(){
|
||||
if (HasCleanedUp)return;
|
||||
|
||||
|
15
Reporter.cs
15
Reporter.cs
@@ -70,5 +70,20 @@ namespace TweetDck{
|
||||
Environment.FailFast(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleEarlyFailure(string caption, string message){
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
FormMessage form = new FormMessage(caption, message, MessageBoxIcon.Error);
|
||||
form.AddButton("Exit");
|
||||
form.ShowDialog();
|
||||
|
||||
try{
|
||||
Process.GetCurrentProcess().Kill();
|
||||
}catch{
|
||||
Environment.FailFast(message, new Exception(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,12 +3,16 @@ Clear columns
|
||||
|
||||
[description]
|
||||
- Adds buttons and keyboard shortcuts to quickly clear columns
|
||||
- Hold Shift when clicking or using a keyboard shortcut to reset the column instead
|
||||
|
||||
[author]
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
1.1
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[requires]
|
||||
1.4.1
|
@@ -11,38 +11,75 @@ enabled(){
|
||||
TD.controller.stats.columnActionClick("clear");
|
||||
};
|
||||
|
||||
var clearAllColumns = () => {
|
||||
Object.keys(TD.controller.columnManager.getAll()).forEach(key => clearColumn(key));
|
||||
var resetColumn = (columnName) => {
|
||||
var col = TD.controller.columnManager.get(columnName);
|
||||
col.model.setClearedTimestamp(0);
|
||||
col.reloadTweets();
|
||||
};
|
||||
|
||||
var forEachColumn = (func) => {
|
||||
Object.keys(TD.controller.columnManager.getAll()).forEach(func);
|
||||
};
|
||||
|
||||
var replaceMustache = (key, search, replace) => {
|
||||
TD.mustaches[key] = TD.mustaches[key].replace(search, replace);
|
||||
};
|
||||
|
||||
var wasShiftPressed = false;
|
||||
|
||||
var updateShiftState = (pressed) => {
|
||||
if (pressed != wasShiftPressed){
|
||||
wasShiftPressed = pressed;
|
||||
|
||||
if (pressed){
|
||||
$(document).on("mousemove", this.eventKeyUp);
|
||||
}
|
||||
else{
|
||||
$(document).off("mousemove", this.eventKeyUp);
|
||||
}
|
||||
|
||||
$("#clear-columns-btn-all").text(pressed ? "Reset all" : "Clear all");
|
||||
}
|
||||
};
|
||||
|
||||
// prepare event handlers
|
||||
this.eventClearSingle = function(){
|
||||
clearColumn($(this).closest(".js-column").attr("data-column"));
|
||||
this.eventClickSingle = function(e){
|
||||
var name = $(this).closest(".js-column").attr("data-column");
|
||||
e.shiftKey ? resetColumn(name) : clearColumn(name);
|
||||
};
|
||||
|
||||
this.eventClearAll = function(){
|
||||
clearAllColumns();
|
||||
this.eventClickAll = function(e){
|
||||
forEachColumn(e.shiftKey ? resetColumn : clearColumn);
|
||||
};
|
||||
|
||||
this.eventKeys = function(e){
|
||||
if (e.keyCode === 46 && (document.activeElement === null || document.activeElement === document.body)){ // 46 = delete
|
||||
this.eventKeyDown = function(e){
|
||||
if (!(document.activeElement === null || document.activeElement === document.body)){
|
||||
return;
|
||||
}
|
||||
|
||||
updateShiftState(e.shiftKey);
|
||||
|
||||
if (e.keyCode === 46){ // 46 = delete
|
||||
if (e.altKey){
|
||||
clearAllColumns();
|
||||
forEachColumn(e.shiftKey ? resetColumn : clearColumn);
|
||||
}
|
||||
else{
|
||||
var focusedColumn = $(".js-column.is-focused");
|
||||
|
||||
if (focusedColumn.length){
|
||||
clearColumn(focusedColumn.attr("data-column"));
|
||||
var name = focusedColumn.attr("data-column");
|
||||
e.shiftKey ? resetColumn(name) : clearColumn(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.eventKeyUp = function(e){
|
||||
if (!e.shiftKey){
|
||||
updateShiftState(false);
|
||||
}
|
||||
};
|
||||
|
||||
// add column buttons and keyboard shortcut info to UI
|
||||
replaceMustache("column/column_header.mustache", "</header>", [
|
||||
'{{^isTemporary}}',
|
||||
@@ -62,27 +99,26 @@ enabled(){
|
||||
].join(""));
|
||||
|
||||
// load custom style
|
||||
var style = document.createElement("style");
|
||||
document.head.appendChild(style);
|
||||
|
||||
var sheet = style.sheet;
|
||||
sheet.insertRule(".column-title { margin-right: 60px !important; }", 0);
|
||||
sheet.insertRule(".column-type-message .column-title { margin-right: 115px !important; }", 0);
|
||||
sheet.insertRule(".mark-all-read-link { right: 59px !important; }", 0);
|
||||
sheet.insertRule(".open-compose-dm-link { right: 90px !important; }", 0);
|
||||
var css = window.TDPF_createCustomStyle(this);
|
||||
css.insert(".column-title { margin-right: 60px !important; }");
|
||||
css.insert(".column-type-message .column-title { margin-right: 115px !important; }");
|
||||
css.insert(".mark-all-read-link { right: 59px !important; }");
|
||||
css.insert(".open-compose-dm-link { right: 90px !important; }");
|
||||
css.insert("button[data-action='clear'].btn-options-tray { display: none !important; }");
|
||||
}
|
||||
|
||||
ready(){
|
||||
// setup events
|
||||
$(document).on("click", "[data-action='td-clearcolumns-dosingle']", this.eventClearSingle);
|
||||
$(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClearAll);
|
||||
$(document).on("keydown", this.eventKeys);
|
||||
$(document).on("click", "[data-action='td-clearcolumns-dosingle']", this.eventClickSingle);
|
||||
$(document).on("click", "[data-action='td-clearcolumns-doall']", this.eventClickAll);
|
||||
$(document).on("keydown", this.eventKeyDown);
|
||||
$(document).on("keyup", this.eventKeyUp);
|
||||
|
||||
// add clear all button
|
||||
$("nav.app-navigator").first().append([
|
||||
'<a class="link-clean cf app-nav-link padding-hl" data-title="Clear all" data-action="td-clearcolumns-doall">',
|
||||
'<div class="obj-left"><i class="icon icon-large icon-clear-timeline"></i></div>',
|
||||
'<div class="nbfc padding-ts hide-condensed">Clear all</div>',
|
||||
'<div id="clear-columns-btn-all" class="nbfc padding-ts hide-condensed">Clear all</div>',
|
||||
'</a></nav>'
|
||||
].join(""));
|
||||
}
|
||||
|
@@ -9,7 +9,10 @@ Revert TweetDeck design changes
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
1.1
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[requires]
|
||||
1.4.1
|
@@ -1,15 +1,11 @@
|
||||
enabled(){
|
||||
// add a stylesheet to change tweet actions
|
||||
var style = document.createElement("style");
|
||||
style.id = "design-revert";
|
||||
document.head.appendChild(style);
|
||||
|
||||
var sheet = style.sheet;
|
||||
sheet.insertRule(".tweet-actions { float: right !important; width: auto !important; }", 0);
|
||||
sheet.insertRule(".tweet-action { opacity: 0; }", 0);
|
||||
sheet.insertRule(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important; }", 0);
|
||||
sheet.insertRule(".tweet:hover .tweet-action, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1; visibility: visible !important; }", 0);
|
||||
sheet.insertRule(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }", 0);
|
||||
this.css = window.TDPF_createCustomStyle(this);
|
||||
this.css.insert(".tweet-actions { float: right !important; width: auto !important; }");
|
||||
this.css.insert(".tweet-action { opacity: 0; }");
|
||||
this.css.insert(".is-favorite .tweet-action, .is-retweet .tweet-action { opacity: 0.5; visibility: visible !important; }");
|
||||
this.css.insert(".tweet:hover .tweet-action, .is-favorite .tweet-action[rel='favorite'], .is-retweet .tweet-action[rel='retweet'] { opacity: 1; visibility: visible !important; }");
|
||||
this.css.insert(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }");
|
||||
|
||||
// revert small links around the tweet
|
||||
this.prevFooterMustache = TD.mustaches["status/tweet_single_footer.mustache"];
|
||||
@@ -31,7 +27,8 @@ ready(){
|
||||
}
|
||||
|
||||
disabled(){
|
||||
$("#design-revert").remove();
|
||||
this.css.remove();
|
||||
|
||||
$(document).off("uiShowActionsMenu", this.uiShowActionsMenuEvent);
|
||||
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
|
||||
}
|
@@ -8,7 +8,7 @@ Custom reply account
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
1.1
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
@@ -1,67 +1,120 @@
|
||||
enabled(){
|
||||
var configuration = { defaultAccount: "" };
|
||||
var configuration = { defaultAccount: "#preferred" };
|
||||
|
||||
window.TDPF_loadConfigurationFile(this, "configuration.js", "configuration.default.js", obj => configuration = obj);
|
||||
|
||||
this.uiInlineComposeTweetEvent = function(e, data){
|
||||
var account = null;
|
||||
this.lastSelectedAccount = null;
|
||||
|
||||
if (configuration.useAdvancedSelector && configuration.customSelector){
|
||||
var column = TD.controller.columnManager.get(data.element.closest("section.column").attr("data-column"));
|
||||
var result = configuration.customSelector(column);
|
||||
|
||||
if (typeof result === "string" && result[0] === '@'){
|
||||
account = result.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (account === null){
|
||||
if (configuration.defaultAccount === false){
|
||||
this.uiComposeTweetEvent = (e, data) => {
|
||||
if (data.type !== "reply" || data.popFromInline || !("element" in data)){
|
||||
return;
|
||||
}
|
||||
else if (configuration.defaultAccount !== "" && configuration.defaultAccount[0] === '@'){
|
||||
account = configuration.defaultAccount.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
var identifier;
|
||||
var query;
|
||||
|
||||
if (account === null){
|
||||
identifier = TD.storage.clientController.client.getDefaultAccount();
|
||||
if (configuration.useAdvancedSelector){
|
||||
if (configuration.customSelector){
|
||||
var column = TD.controller.columnManager.get(data.element.closest("section.column").attr("data-column"));
|
||||
query = configuration.customSelector(column);
|
||||
}
|
||||
else{
|
||||
var obj = TD.storage.accountController.getAccountFromUsername(account);
|
||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: useAdvancedSelector is true, but customSelector function is missing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else{
|
||||
query = configuration.defaultAccount;
|
||||
|
||||
if (query === ""){
|
||||
query = "#preferred";
|
||||
}
|
||||
else if (typeof query !== "string"){
|
||||
query = "#default";
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof query === "undefined"){
|
||||
query = "#preferred";
|
||||
}
|
||||
|
||||
if (typeof query !== "string"){
|
||||
return;
|
||||
}
|
||||
else if (query.length === 0){
|
||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account is empty");
|
||||
return;
|
||||
}
|
||||
else if (query[0] !== '@' && query[0] !== '#'){
|
||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: the requested account does not begin with @ or #: "+query);
|
||||
return;
|
||||
}
|
||||
|
||||
var identifier = null;
|
||||
|
||||
switch(query){
|
||||
case "#preferred":
|
||||
identifier = TD.storage.clientController.client.getDefaultAccount();
|
||||
break;
|
||||
|
||||
case "#last":
|
||||
if (this.lastSelectedAccount === null){
|
||||
return;
|
||||
}
|
||||
|
||||
identifier = this.lastSelectedAccount;
|
||||
break;
|
||||
|
||||
case "#default":
|
||||
return;
|
||||
|
||||
default:
|
||||
if (query[0] === '@'){
|
||||
var obj = TD.storage.accountController.getAccountFromUsername(query.substring(1));
|
||||
|
||||
if (obj.length === 0){
|
||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: requested account not found: "+query);
|
||||
return;
|
||||
}
|
||||
else{
|
||||
identifier = obj[0].privateState.key;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$TD.alert("warning", "Plugin reply-account has invalid configuration: unknown requested account query: "+query);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data.singleFrom = data.from = [ identifier ];
|
||||
};
|
||||
|
||||
this.onSelectedAccountChanged = () => {
|
||||
var selected = $(".js-account-item.is-selected", ".js-account-list");
|
||||
this.lastSelectedAccount = selected.length === 1 ? selected.attr("data-account-key") : null;
|
||||
};
|
||||
}
|
||||
|
||||
ready(){
|
||||
var events = $._data(document, "events");
|
||||
|
||||
if ("uiInlineComposeTweet" in events){
|
||||
$(document).on("uiInlineComposeTweet", this.uiInlineComposeTweetEvent);
|
||||
for(var event of [ "uiInlineComposeTweet", "uiDockedComposeTweet" ]){
|
||||
$(document).on(event, this.uiComposeTweetEvent);
|
||||
|
||||
var handlers = events["uiInlineComposeTweet"];
|
||||
var oldHandler = handlers[0];
|
||||
var newHandler = handlers[1];
|
||||
var handlers = events[event];
|
||||
var newHandler = handlers[handlers.length-1];
|
||||
|
||||
for(var index = handlers.length-1; index > 0; index--){
|
||||
handlers[index] = handlers[index-1];
|
||||
}
|
||||
|
||||
handlers[0] = newHandler;
|
||||
handlers[1] = oldHandler;
|
||||
}
|
||||
else{
|
||||
$(document).on("uiInlineComposeTweet", this.uiInlineComposeTweetEvent);
|
||||
}
|
||||
|
||||
$(document).on("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
|
||||
}
|
||||
|
||||
disabled(){
|
||||
$(document).off("uiInlineComposeTweet", this.uiInlineComposeTweetEvent);
|
||||
$(document).off("uiInlineComposeTweet", this.uiComposeTweetEvent);
|
||||
$(document).off("uiDockedComposeTweet", this.uiComposeTweetEvent);
|
||||
$(document).off("click", ".js-account-list .js-account-item", this.onSelectedAccountChanged);
|
||||
}
|
@@ -13,13 +13,14 @@
|
||||
*
|
||||
* Set value of 'defaultAccount' to one of the following values:
|
||||
*
|
||||
* "" to use your preferred TweetDeck account for all replies (default)
|
||||
* "#preferred" to use your preferred TweetDeck account (the one used to log into TweetDeck)
|
||||
* "#last" to specify the account that was selected last time (only updates if a single account is selected)
|
||||
* "#default" to fall back to default TweetDeck behavior; useful for advanced configuration below, otherwise disable the plugin instead
|
||||
* "@myAccount" to specify an account name to use; has to be one of your registered account names
|
||||
* false to fall back to default TweetDeck behavior; useful for advanced configuration below, otherwise disable the plugin instead
|
||||
*
|
||||
*/
|
||||
|
||||
defaultAccount: "",
|
||||
defaultAccount: "#preferred",
|
||||
|
||||
/*
|
||||
* Advanced way of configuring the plugin
|
||||
@@ -30,8 +31,8 @@
|
||||
* 1. Set value of 'useAdvancedSelector' to true
|
||||
* 2. Uncomment the 'customSelector' function, and replace the example code with your desired behavior
|
||||
*
|
||||
* If 'customSelector' returns a string containing a full name of one of the registered accounts (including @), that account is used.
|
||||
* If it returns anything else (for example false, undefined, or an account name that is not registered), it falls back to 'defaultAccount' behavior.
|
||||
* The 'customSelector' function should return a string in one of the formats supported by 'defaultAccount'.
|
||||
* If it returns anything else (for example, false or undefined), it falls back to 'defaultAccount' behavior.
|
||||
*
|
||||
* The 'column' parameter is a TweetDeck column object. If you want to see all properties of the object, open your browser, nagivate to TweetDeck,
|
||||
* log in, and run the following code in your browser console, which will return an object containing all of the column objects mapped to their IDs:
|
||||
|
18
Resources/Plugins/timeline-polls/.meta
Normal file
18
Resources/Plugins/timeline-polls/.meta
Normal file
@@ -0,0 +1,18 @@
|
||||
[name]
|
||||
Polls in timelines
|
||||
|
||||
[description]
|
||||
- Adds poll result display directly into timelines
|
||||
- Experimental, may be buggy or break when TweetDeck updates
|
||||
|
||||
[author]
|
||||
chylex
|
||||
|
||||
[version]
|
||||
1.0
|
||||
|
||||
[website]
|
||||
https://tweetduck.chylex.com
|
||||
|
||||
[requires]
|
||||
1.4.1
|
25
Resources/Plugins/timeline-polls/browser.js
Normal file
25
Resources/Plugins/timeline-polls/browser.js
Normal file
@@ -0,0 +1,25 @@
|
||||
constructor(){
|
||||
super({
|
||||
requiresPageReload: true
|
||||
});
|
||||
}
|
||||
|
||||
enabled(){
|
||||
// add a stylesheet
|
||||
window.TDPF_createCustomStyle(this).insert(".column-detail .timeline-poll-container { display: none }");
|
||||
|
||||
// setup layout injecting
|
||||
var injectLayout = (mustache, onlyIfNotFound, search, replace) => {
|
||||
if (TD.mustaches[mustache].indexOf(onlyIfNotFound) === -1){
|
||||
TD.mustaches[mustache] = TD.mustaches[mustache].replace(search, replace);
|
||||
}
|
||||
};
|
||||
|
||||
// add poll rendering to tweets
|
||||
injectLayout("status/tweet_single.mustache", "status/poll", "{{/quotedTweetMissing}} {{#translation}}", "{{/quotedTweetMissing}} <div class='timeline-poll-container'>{{>duck/tweet_single/poll}}</div> {{#translation}}");
|
||||
TD.mustaches["duck/tweet_single/poll.mustache"] = '<div class="js-poll margin-tl"> {{#poll}} <ul class="margin-b--12"> {{#choices}} <li class="position-rel margin-b--8 height-3"> <div class="poll-bar pin-top height-p--100 br-1 {{#isWinner}}poll-bar--winner{{/isWinner}} {{#hasTimeLeft}}br-left{{/hasTimeLeft}} width-p--{{percentage}}"/> <div class="poll-label position-rel padding-a--4"> <span class="txt-bold txt-right inline-block width-5 padding-r--4">{{percentage}}%</span> {{{label}}} {{#isSelectedChoice}} <i class="icon icon-check txt-size-variable--11"></i> {{/isSelectedChoice}} </div> </li> {{/choices}} </ul> <span class="inline-block txt-small padding-ls txt-seamful-deep-gray"> {{{prettyCount}}} · {{#hasTimeLeft}} {{{prettyTimeLeft}}} {{/hasTimeLeft}} {{^hasTimeLeft}} {{_i}}Final results{{/i}} {{/hasTimeLeft}} </span> {{/poll}} </div>';
|
||||
}
|
||||
|
||||
disabled(){
|
||||
// not needed, plugin reloads the page when enabled or disabled
|
||||
}
|
@@ -104,11 +104,14 @@
|
||||
var html = $(tweet.render({
|
||||
withFooter: false,
|
||||
withTweetActions: false,
|
||||
withMediaPreview: false
|
||||
withMediaPreview: true,
|
||||
isMediaPreviewOff: true,
|
||||
isMediaPreviewSmall: false,
|
||||
isMediaPreviewLarge: false
|
||||
}));
|
||||
|
||||
html.css("border", "0");
|
||||
html.find(".tweet-body").first().children("footer").remove();
|
||||
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
||||
|
||||
var url = html.find("time").first().children("a").first().attr("href") || "";
|
||||
|
||||
@@ -376,26 +379,39 @@
|
||||
//
|
||||
// Block: Support for extra mouse buttons.
|
||||
//
|
||||
(function(){
|
||||
var tryClickSelector = function(selector, parent){
|
||||
return $(selector, parent).click().length;
|
||||
};
|
||||
|
||||
var tryCloseModal = function(){
|
||||
var modal = $("#open-modal");
|
||||
return modal.is(":visible") && tryClickSelector("a[rel=dismiss]", modal);
|
||||
};
|
||||
|
||||
var tryCloseHighlightedColumn = function(){
|
||||
if (highlightedColumnEle){
|
||||
var column = highlightedColumnEle.closest(".js-column");
|
||||
return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
|
||||
}
|
||||
};
|
||||
|
||||
window.TDGF_onMouseClickExtra = function(button){
|
||||
if (button === 1){ // back button
|
||||
var modal = $("#open-modal");
|
||||
|
||||
if (highlightedColumnEle && highlightedColumnEle.closest(".js-column").is(".is-shifted-1")){
|
||||
highlightedColumnEle.find(".js-column-back").first().click();
|
||||
}
|
||||
else if (modal.is(":visible")){
|
||||
modal.find("a[rel=dismiss]").click();
|
||||
}
|
||||
else{
|
||||
tryCloseModal() ||
|
||||
tryClickSelector(".js-inline-compose-close") ||
|
||||
tryCloseHighlightedColumn() ||
|
||||
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
|
||||
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back") ||
|
||||
$(".js-column-back").click();
|
||||
}
|
||||
}
|
||||
else if (button === 2){ // forward button
|
||||
if (highlightedTweetEle){
|
||||
highlightedTweetEle.children().first().click();
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
//
|
||||
// Block: Fix scheduled tweets not showing up sometimes.
|
||||
|
@@ -114,9 +114,39 @@
|
||||
var account = embedded[0].getElementsByClassName("account-link");
|
||||
if (account.length === 0)return;
|
||||
|
||||
$TD.setNotificationTweetEmbedded(account[0].getAttribute("href")+"/status/"+tweetId);
|
||||
$TD.setNotificationQuotedTweet(account[0].getAttribute("href")+"/status/"+tweetId);
|
||||
})();
|
||||
|
||||
//
|
||||
// Block: Setup a skip button.
|
||||
//
|
||||
(function(){
|
||||
if (document.body.hasAttribute("td-example-notification")){
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.insertAdjacentHTML("afterbegin", [
|
||||
'<svg id="td-skip" xmlns="http://www.w3.org/2000/svg" width="10" height="17" viewBox="0 0 350 600" style="position:fixed;left:30px;bottom:10px;z-index:1000">',
|
||||
'<path fill="#888" d="M0,151.656l102.208-102.22l247.777,247.775L102.208,544.986L0,442.758l145.546-145.547">',
|
||||
'</svg>'
|
||||
].join(""));
|
||||
|
||||
document.getElementById("td-skip").addEventListener("click", function(){
|
||||
$TD.loadNextNotification();
|
||||
});
|
||||
})();
|
||||
|
||||
//
|
||||
// Block: Setup a hover class on body.
|
||||
//
|
||||
document.body.addEventListener("mouseenter", function(){
|
||||
document.body.classList.add("td-hover");
|
||||
});
|
||||
|
||||
document.body.addEventListener("mouseleave", function(){
|
||||
document.body.classList.remove("td-hover");
|
||||
});
|
||||
|
||||
//
|
||||
// Block: Page fully loaded.
|
||||
//
|
||||
|
@@ -15,7 +15,7 @@
|
||||
try{
|
||||
obj = eval("("+contents+")");
|
||||
}catch(err){
|
||||
if (!(onFailure && onFailure(err.message))){
|
||||
if (!(onFailure && onFailure(err))){
|
||||
$TD.alert("warning", "Problem loading '"+fileName+"' file for '"+identifier+"' plugin, the JavaScript syntax is invalid: "+err.message);
|
||||
}
|
||||
|
||||
@@ -34,4 +34,21 @@
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// Block: Setup a function to add/remove custom CSS.
|
||||
//
|
||||
window.TDPF_createCustomStyle = function(pluginObject){
|
||||
var element = document.createElement("style");
|
||||
element.id = "plugin-"+pluginObject.$id+"-"+Math.random().toString(36).substring(2, 7);
|
||||
document.head.appendChild(element);
|
||||
|
||||
var obj = {
|
||||
insert: (rule) => element.sheet.insertRule(rule, 0),
|
||||
remove: () => $(element).remove()
|
||||
};
|
||||
|
||||
obj.element = element;
|
||||
return obj;
|
||||
};
|
||||
})($TDP);
|
@@ -19,7 +19,6 @@
|
||||
//
|
||||
var createUpdateNotificationElement = function(version, download){
|
||||
var outdated = version === "unsupported";
|
||||
var tweetdick = version === "tweetdick";
|
||||
|
||||
var ele = $("#tweetdck-update");
|
||||
var existed = ele.length > 0;
|
||||
@@ -37,15 +36,6 @@
|
||||
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</span></button>",
|
||||
"</div>",
|
||||
"</div>"
|
||||
] : tweetdick ? [
|
||||
"<div id='tweetdck-update'>",
|
||||
"<p class='tdu-title'>TweetDick Ending</p>",
|
||||
"<p class='tdu-info'>Please, move to TweetDuck.</p>",
|
||||
"<div class='tdu-buttons'>",
|
||||
"<button class='btn btn-positive tdu-btn-unsupported'><span class='label'>Read More</span></button>",
|
||||
"<button class='btn btn-negative tdu-btn-dismiss'><span class='label'>Dismiss</span></button>",
|
||||
"</div>",
|
||||
"</div>"
|
||||
] : [
|
||||
"<div id='tweetdck-update'>",
|
||||
"<p class='tdu-title'>"+$TDU.brandName+" Update</p>",
|
||||
@@ -118,10 +108,6 @@
|
||||
$TDU.openBrowser("https://github.com/chylex/TweetDuck/wiki/Supported-Systems");
|
||||
});
|
||||
|
||||
buttonDiv.children(".tdu-btn-tweetdick").click(function(){
|
||||
$TDU.openBrowser("https://github.com/chylex/TweetDick/wiki/Future-of-TweetDick");
|
||||
});
|
||||
|
||||
buttonDiv.children(".tdu-btn-dismiss,.tdu-btn-unsupported").click(function(){
|
||||
$TDU.onUpdateDismissed(version);
|
||||
ele.slideUp(function(){ ele.remove(); });
|
||||
@@ -145,13 +131,6 @@
|
||||
|
||||
return;
|
||||
}
|
||||
else if ($TDU.brandName === "TweetDick"){
|
||||
if ($TDU.dismissedVersionTag !== "tweetdick"){
|
||||
createUpdateNotificationElement("tweetdick");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(updateCheckTimeoutID);
|
||||
updateCheckTimeoutID = setTimeout(runUpdateCheck, 1000*60*60); // 1 hour
|
||||
|
@@ -176,6 +176,7 @@
|
||||
<Compile Include="Core\Other\Settings\TabSettingsUpdates.Designer.cs">
|
||||
<DependentUpon>TabSettingsUpdates.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Core\Utils\CommandLineArgs.cs" />
|
||||
<Compile Include="Core\Utils\CommandLineArgsParser.cs" />
|
||||
<Compile Include="Core\Utils\WindowState.cs" />
|
||||
<Compile Include="Core\Utils\WindowsUtils.cs" />
|
||||
@@ -192,6 +193,7 @@
|
||||
<Compile Include="Core\Other\FormSettings.Designer.cs">
|
||||
<DependentUpon>FormSettings.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migration\MigrationUtils.cs" />
|
||||
<Compile Include="Plugins\Controls\PluginControl.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
@@ -234,15 +236,8 @@
|
||||
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
|
||||
<Compile Include="Updates\UpdateHandler.cs" />
|
||||
<Compile Include="Updates\UpdateInfo.cs" />
|
||||
<Compile Include="Migration\FormMigrationQuestion.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Migration\FormMigrationQuestion.Designer.cs">
|
||||
<DependentUpon>FormMigrationQuestion.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Migration\MigrationDecision.cs" />
|
||||
<Compile Include="Migration\MigrationManager.cs" />
|
||||
<Compile Include="Migration\Helpers\ProgramRegistrySearch.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
|
@@ -1,5 +1,6 @@
|
||||
del "bin\x86\Release\*.xml"
|
||||
del "bin\x86\Release\devtools_resources.pak"
|
||||
del "bin\x86\Release\d3dcompiler_43.dll"
|
||||
|
||||
del "bin\x86\Release\TweetDuck.Browser.exe"
|
||||
ren "bin\x86\Release\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"
|
9
bld/Resources/LICENSE
Normal file
9
bld/Resources/LICENSE
Normal file
@@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
BIN
bld/Resources/icon.ico
Normal file
BIN
bld/Resources/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
161
bld/gen_full.iss
Normal file
161
bld/gen_full.iss
Normal file
@@ -0,0 +1,161 @@
|
||||
; Script generated by the Inno Script Studio Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "TweetDuck"
|
||||
#define MyAppPublisher "chylex"
|
||||
#define MyAppURL "https://tweetduck.chylex.com"
|
||||
#define MyAppExeName "TweetDuck.exe"
|
||||
|
||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||
|
||||
[Setup]
|
||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
OutputBaseFilename={#MyAppName}
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
LicenseFile=.\Resources\LICENSE
|
||||
SetupIconFile=.\Resources\icon.ico
|
||||
Uninstallable=TDIsUninstallable
|
||||
UninstallDisplayName={#MyAppName}
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
InternalCompressLevel=max
|
||||
MinVersion=0,6.1
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Tasks]
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
|
||||
|
||||
[Files]
|
||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,devtools_resources.pak,d3dcompiler_43.dll"
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: TDIsUninstallable
|
||||
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec skipifsilent
|
||||
|
||||
[InstallDelete]
|
||||
Type: files; Name: "{app}\td-log.txt"
|
||||
|
||||
[UninstallDelete]
|
||||
Type: files; Name: "{app}\debug.log"
|
||||
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
|
||||
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
||||
|
||||
[Code]
|
||||
var IsPortableInstallation: Boolean;
|
||||
var UpdatePath: String;
|
||||
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
IsPortableInstallation := ExpandConstant('{param:PORTABLEINSTALL}') = '1'
|
||||
UpdatePath := ExpandConstant('{param:UPDATEPATH}')
|
||||
|
||||
if IsPortableInstallation and (UpdatePath = '') then
|
||||
begin
|
||||
MsgBox('The /PORTABLEINSTALL flag requires the /UPDATEPATH parameter.', mbCriticalError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if TDGetNetFrameworkVersion() >= 379893 then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ Set the installation path if updating. }
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
if (UpdatePath <> '') then
|
||||
begin
|
||||
WizardForm.DirEdit.Text := UpdatePath;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Skip the install path selection page if running from an update installer. }
|
||||
function ShouldSkipPage(PageID: Integer): Boolean;
|
||||
begin
|
||||
Result := (PageID = wpSelectDir) and (UpdatePath <> '')
|
||||
end;
|
||||
|
||||
{ Ask user if they want to delete 'AppData\TweetDuck' and 'plugins' folders after uninstallation. }
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
var ProfileDataFolder: String;
|
||||
var PluginDataFolder: String;
|
||||
|
||||
begin
|
||||
if CurUninstallStep = usPostUninstall then
|
||||
begin
|
||||
ProfileDataFolder := ExpandConstant('{localappdata}\{#MyAppName}')
|
||||
PluginDataFolder := ExpandConstant('{app}\plugins')
|
||||
|
||||
if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then
|
||||
begin
|
||||
DelTree(ProfileDataFolder, True, True, True);
|
||||
DelTree(PluginDataFolder, True, True, True);
|
||||
DelTree(ExpandConstant('{app}'), True, False, False);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Create a 'makeportable' file if running in portable mode. }
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if (CurStep = ssPostInstall) and IsPortableInstallation then
|
||||
begin
|
||||
while not SaveStringToFile(ExpandConstant('{app}\makeportable'), '', False) do
|
||||
begin
|
||||
if MsgBox('Could not create a ''makeportable'' file in the installation folder. If the file is not present, the installation will not be fully portable.', mbCriticalError, MB_RETRYCANCEL) <> IDRETRY then
|
||||
begin
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Returns true if the installer should create uninstallation entries (i.e. not running in portable or full update mode). }
|
||||
function TDIsUninstallable: Boolean;
|
||||
begin
|
||||
Result := (UpdatePath = '') and not IsPortableInstallation
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := 0;
|
||||
end;
|
150
bld/gen_port.iss
Normal file
150
bld/gen_port.iss
Normal file
@@ -0,0 +1,150 @@
|
||||
; Script generated by the Inno Script Studio Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "TweetDuck"
|
||||
#define MyAppPublisher "chylex"
|
||||
#define MyAppURL "https://tweetduck.chylex.com"
|
||||
#define MyAppExeName "TweetDuck.exe"
|
||||
|
||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||
|
||||
[Setup]
|
||||
AppId={{8C25A716-7E11-4AAD-9992-8B5D0C78AE06}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
OutputBaseFilename={#MyAppName}.Portable
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
LicenseFile=.\Resources\LICENSE
|
||||
SetupIconFile=.\Resources\icon.ico
|
||||
Uninstallable=no
|
||||
UsePreviousAppDir=no
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
InternalCompressLevel=max
|
||||
MinVersion=0,6.1
|
||||
|
||||
#include <idp.iss>
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec
|
||||
|
||||
[Code]
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
function TDGetAppVersionClean: String; forward;
|
||||
procedure TDExecuteFullDownload; forward;
|
||||
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2, and prepare full download package. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/{#MyAppName}.exe', ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
||||
|
||||
if TDGetNetFrameworkVersion() >= 379893 then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ Prepare download plugin if there are any files to download, and set the installation path. }
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
idpDownloadAfter(wpReady);
|
||||
end;
|
||||
|
||||
{ Remove uninstallation data and application to force them to be replaced with updated ones. }
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssInstall then
|
||||
begin
|
||||
TDExecuteFullDownload();
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
{ Return a cleaned up form of the app version string (removes all .0 suffixes). }
|
||||
function TDGetAppVersionClean: String;
|
||||
var Substr: String;
|
||||
var CleanVersion: String;
|
||||
|
||||
begin
|
||||
CleanVersion := '{#MyAppVersion}'
|
||||
|
||||
while True do
|
||||
begin
|
||||
Substr := Copy(CleanVersion, Length(CleanVersion)-1, 2);
|
||||
|
||||
if (CompareStr(Substr, '.0') <> 0) then
|
||||
begin
|
||||
break;
|
||||
end;
|
||||
|
||||
CleanVersion := Copy(CleanVersion, 1, Length(CleanVersion)-2);
|
||||
end;
|
||||
|
||||
Result := CleanVersion;
|
||||
end;
|
||||
|
||||
{ Run the full package installer if downloaded. }
|
||||
procedure TDExecuteFullDownload;
|
||||
var InstallFile: String;
|
||||
var ResultCode: Integer;
|
||||
|
||||
begin
|
||||
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.Full.exe')
|
||||
WizardForm.ProgressGauge.Style := npbstMarquee;
|
||||
|
||||
try
|
||||
if Exec(InstallFile, '/SP- /SILENT /MERGETASKS="!desktopicon" /UPDATEPATH="'+ExpandConstant('{app}\')+'" /PORTABLEINSTALL=1', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
|
||||
if ResultCode <> 0 then
|
||||
begin
|
||||
DeleteFile(InstallFile);
|
||||
Abort();
|
||||
Exit;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
MsgBox('Could not run the full installer in portable mode. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
|
||||
|
||||
DeleteFile(InstallFile);
|
||||
Abort();
|
||||
Exit;
|
||||
end;
|
||||
finally
|
||||
WizardForm.ProgressGauge.Style := npbstNormal;
|
||||
DeleteFile(InstallFile);
|
||||
end;
|
||||
end;
|
264
bld/gen_upd.iss
Normal file
264
bld/gen_upd.iss
Normal file
@@ -0,0 +1,264 @@
|
||||
; Script generated by the Inno Script Studio Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define MyAppName "TweetDuck"
|
||||
#define MyAppPublisher "chylex"
|
||||
#define MyAppURL "https://tweetduck.chylex.com"
|
||||
#define MyAppExeName "TweetDuck.exe"
|
||||
|
||||
#define MyAppID "8C25A716-7E11-4AAD-9992-8B5D0C78AE06"
|
||||
#define MyAppVersion GetFileVersion("..\bin\x86\Release\TweetDuck.exe")
|
||||
#define CefVersion "3.2785.1478.0"
|
||||
|
||||
[Setup]
|
||||
AppId={{{#MyAppID}}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
AppSupportURL={#MyAppURL}
|
||||
AppUpdatesURL={#MyAppURL}
|
||||
DefaultDirName={pf}\{#MyAppName}
|
||||
DefaultGroupName={#MyAppName}
|
||||
OutputBaseFilename={#MyAppName}.Update
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
LicenseFile=.\Resources\LICENSE
|
||||
SetupIconFile=.\Resources\icon.ico
|
||||
Uninstallable=TDIsUninstallable
|
||||
UninstallDisplayName={#MyAppName}
|
||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||
Compression=lzma
|
||||
SolidCompression=yes
|
||||
InternalCompressLevel=max
|
||||
MinVersion=0,6.1
|
||||
|
||||
#include <idp.iss>
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
||||
[Files]
|
||||
Source: "..\bin\x86\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "..\bin\x86\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "*.xml,*.dll,*.pak,*.bin,*.dat"
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall shellexec
|
||||
|
||||
[InstallDelete]
|
||||
Type: files; Name: "{app}\*.xml"
|
||||
Type: files; Name: "{app}\*.js"
|
||||
Type: files; Name: "{app}\d3dcompiler_43.dll"
|
||||
Type: files; Name: "{app}\devtools_resources.pak"
|
||||
Type: files; Name: "{app}\CefSharp.BrowserSubprocess.exe"
|
||||
Type: files; Name: "{app}\td-log.txt"
|
||||
Type: files; Name: "{app}\debug.log"
|
||||
Type: files; Name: "{localappdata}\{#MyAppName}\ChromeDWriteFontCache"
|
||||
|
||||
[UninstallDelete]
|
||||
Type: files; Name: "{app}\*.*"
|
||||
Type: filesandordirs; Name: "{app}\locales"
|
||||
Type: filesandordirs; Name: "{app}\scripts"
|
||||
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\Cache"
|
||||
Type: filesandordirs; Name: "{localappdata}\{#MyAppName}\GPUCache"
|
||||
|
||||
[Code]
|
||||
function TDIsUninstallable: Boolean; forward;
|
||||
function TDFindUpdatePath: String; forward;
|
||||
function TDGetNetFrameworkVersion: Cardinal; forward;
|
||||
function TDGetAppVersionClean: String; forward;
|
||||
function TDIsMatchingCEFVersion: Boolean; forward;
|
||||
procedure TDExecuteFullDownload; forward;
|
||||
|
||||
var IsPortable: Boolean;
|
||||
var UpdatePath: String;
|
||||
|
||||
{ Check .NET Framework version on startup, ask user if they want to proceed if older than 4.5.2. Prepare full download package if required. }
|
||||
function InitializeSetup: Boolean;
|
||||
begin
|
||||
IsPortable := ExpandConstant('{param:PORTABLE}') = '1'
|
||||
UpdatePath := TDFindUpdatePath()
|
||||
|
||||
if UpdatePath = '' then
|
||||
begin
|
||||
MsgBox('{#MyAppName} installation could not be found on your system.', mbCriticalError, MB_OK);
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if not TDIsMatchingCEFVersion() then
|
||||
begin
|
||||
idpAddFile('https://github.com/{#MyAppPublisher}/{#MyAppName}/releases/download/'+TDGetAppVersionClean()+'/{#MyAppName}.exe', ExpandConstant('{tmp}\{#MyAppName}.Full.exe'));
|
||||
end;
|
||||
|
||||
if TDGetNetFrameworkVersion() >= 379893 then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if (MsgBox('{#MyAppName} requires .NET Framework 4.5.2 or newer,'+#13+#10+'please download it from {#MyAppURL}'+#13+#10+#13+#10'Do you want to proceed with the setup anyway?', mbCriticalError, MB_YESNO or MB_DEFBUTTON2) = IDNO) then
|
||||
begin
|
||||
Result := False;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
{ Prepare download plugin if there are any files to download, and set the installation path. }
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
WizardForm.DirEdit.Text := UpdatePath;
|
||||
|
||||
if idpFilesCount <> 0 then
|
||||
begin
|
||||
idpDownloadAfter(wpReady);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Ask user if they want to delete 'AppData\TweetDuck' and 'plugins' folders after uninstallation. }
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
var ProfileDataFolder: String;
|
||||
var PluginDataFolder: String;
|
||||
|
||||
begin
|
||||
if CurUninstallStep = usPostUninstall then
|
||||
begin
|
||||
ProfileDataFolder := ExpandConstant('{localappdata}\{#MyAppName}');
|
||||
PluginDataFolder := ExpandConstant('{app}\plugins');
|
||||
|
||||
if (DirExists(ProfileDataFolder) or DirExists(PluginDataFolder)) and (MsgBox('Do you also want to delete your {#MyAppName} profile and plugins?', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) = IDYES) then
|
||||
begin
|
||||
DelTree(ProfileDataFolder, True, True, True);
|
||||
DelTree(PluginDataFolder, True, True, True);
|
||||
DelTree(ExpandConstant('{app}'), True, False, False);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Remove uninstallation data and application to force them to be replaced with updated ones. }
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
begin
|
||||
if CurStep = ssInstall then
|
||||
begin
|
||||
TDExecuteFullDownload();
|
||||
|
||||
if TDIsUninstallable() then
|
||||
begin
|
||||
DeleteFile(ExpandConstant('{app}\unins000.dat'));
|
||||
DeleteFile(ExpandConstant('{app}\unins000.exe'));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Returns true if the installer should create uninstallation entries (i.e. not running in portable mode). }
|
||||
function TDIsUninstallable: Boolean;
|
||||
begin
|
||||
Result := not IsPortable
|
||||
end;
|
||||
|
||||
{ Returns a validated installation path (including trailing backslash) using the /UPDATEPATH parameter or installation info in registry. Returns empty string on failure. }
|
||||
function TDFindUpdatePath: String;
|
||||
var Path: String;
|
||||
|
||||
begin
|
||||
Path := ExpandConstant('{param:UPDATEPATH}')
|
||||
|
||||
if (Path = '') and not IsPortable and not RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyAppID}}_is1', 'InstallLocation', Path) then
|
||||
begin
|
||||
Result := ''
|
||||
Exit
|
||||
end;
|
||||
|
||||
if not FileExists(Path+'{#MyAppExeName}') then
|
||||
begin
|
||||
Result := ''
|
||||
Exit
|
||||
end;
|
||||
|
||||
Result := Path
|
||||
end;
|
||||
|
||||
{ Return DWORD value containing the build version of .NET Framework. }
|
||||
function TDGetNetFrameworkVersion: Cardinal;
|
||||
var FrameworkVersion: Cardinal;
|
||||
|
||||
begin
|
||||
if RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', FrameworkVersion) then
|
||||
begin
|
||||
Result := FrameworkVersion;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
{ Return whether the version of the installed libcef.dll library matches internal one. }
|
||||
function TDIsMatchingCEFVersion: Boolean;
|
||||
var CEFVersion: String;
|
||||
|
||||
begin
|
||||
Result := (GetVersionNumbersString(UpdatePath+'libcef.dll', CEFVersion) and (CompareStr(CEFVersion, '{#CefVersion}') = 0))
|
||||
end;
|
||||
|
||||
{ Return a cleaned up form of the app version string (removes all .0 suffixes). }
|
||||
function TDGetAppVersionClean: String;
|
||||
var Substr: String;
|
||||
var CleanVersion: String;
|
||||
|
||||
begin
|
||||
CleanVersion := '{#MyAppVersion}'
|
||||
|
||||
while True do
|
||||
begin
|
||||
Substr := Copy(CleanVersion, Length(CleanVersion)-1, 2);
|
||||
|
||||
if (CompareStr(Substr, '.0') <> 0) then
|
||||
begin
|
||||
break;
|
||||
end;
|
||||
|
||||
CleanVersion := Copy(CleanVersion, 1, Length(CleanVersion)-2);
|
||||
end;
|
||||
|
||||
Result := CleanVersion;
|
||||
end;
|
||||
|
||||
{ Run the full package installer if downloaded. }
|
||||
procedure TDExecuteFullDownload;
|
||||
var InstallFile: String;
|
||||
var ResultCode: Integer;
|
||||
|
||||
begin
|
||||
InstallFile := ExpandConstant('{tmp}\{#MyAppName}.Full.exe')
|
||||
|
||||
if FileExists(InstallFile) then
|
||||
begin
|
||||
WizardForm.ProgressGauge.Style := npbstMarquee;
|
||||
|
||||
try
|
||||
if Exec(InstallFile, '/SP- /SILENT /MERGETASKS="!desktopicon" /UPDATEPATH="'+UpdatePath+'"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
|
||||
if ResultCode <> 0 then
|
||||
begin
|
||||
DeleteFile(InstallFile);
|
||||
Abort();
|
||||
Exit;
|
||||
end;
|
||||
end else
|
||||
begin
|
||||
MsgBox('Could not run the full installer, please visit {#MyAppURL} and download the latest version manually. Error: '+SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
|
||||
|
||||
DeleteFile(InstallFile);
|
||||
Abort();
|
||||
Exit;
|
||||
end;
|
||||
finally
|
||||
WizardForm.ProgressGauge.Style := npbstNormal;
|
||||
DeleteFile(InstallFile);
|
||||
end;
|
||||
end;
|
||||
end;
|
Reference in New Issue
Block a user