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

Compare commits

..

97 Commits
1.2.1 ... 1.3

Author SHA1 Message Date
3f5cc24a10 Release 1.3 2016-07-06 15:51:08 +02:00
109a330c54 Address ReSharper inspections and refactor FormBrowser constructor 2016-07-06 15:38:47 +02:00
3e4cd3c92f Re-add the link click event to notification.js to fix certain link types 2016-07-06 02:11:45 +02:00
c6aaa4fb04 Revert "Hopefully fix a completely random bug that opened links in notifications in the popup window?"
This reverts commit 2de9570c38.
2016-07-06 02:04:26 +02:00
2de9570c38 Hopefully fix a completely random bug that opened links in notifications in the popup window? 2016-07-06 00:33:00 +02:00
3d88f57f8a Fix video element replacement generating unclickable <a> tags 2016-07-06 00:19:10 +02:00
23606f6d6c Prevent Updates Settings tab from triggering the event for update checks with ID 0 2016-07-05 21:37:15 +02:00
5a1ba1b0dc Add -log program argument to enable JS logging 2016-07-05 20:08:28 +02:00
ced5a82e07 Fix example notification in Settings not showing up the first time 2016-07-05 19:07:51 +02:00
b4fc522f37 Rewrite notification bridge handling and add OnNotificationReady to show notification after it has loaded 2016-07-05 18:31:29 +02:00
a7e222f2e7 Fix display timer setting that was broken in previous update and optimize notification resizing 2016-07-04 17:23:52 +02:00
511e5c483c Make Settings tab element location consistent 2016-07-04 16:42:40 +02:00
a28fddbc4d Replace non-functioning videos on the website with links 2016-07-04 15:37:02 +02:00
780908f777 Refactor some code and replace the only RichTextLabel use with Label 2016-07-04 03:53:44 +02:00
88e5681155 Add import, export, and reset buttons to FormSettings 2016-07-03 23:12:41 +02:00
bc197e6942 Allow Settings and Plugins context options to show up even when not logged in 2016-07-03 23:05:41 +02:00
afbfea6d43 Add methods for config reloading and resetting 2016-07-03 23:05:14 +02:00
662ee7382c Add ActiveButton property to TabPanel 2016-07-03 23:01:26 +02:00
12144a2bda Always display example notification if the Settings Notifications tab is active 2016-07-03 18:25:31 +02:00
e11f2e54b4 Push automatic designer file changes in TabSettingsNotifications 2016-07-03 18:22:33 +02:00
01cb4e32eb Rename the browser subprocess 2016-07-03 18:04:35 +02:00
1ca3541b34 Redesign and rewrite FormSettings to use TabPanel 2016-07-03 14:56:56 +02:00
e209085960 Fix errors in ControlExtensions.InvokeSafe after the previous commit 2016-07-03 14:16:14 +02:00
d1a90612c2 Change ControlExtensions.InvokeSafe to use Control instead of Form 2016-07-03 14:11:42 +02:00
0a21c61535 Rewrite FormPlugins to use the new TabPanel and remove PluginListControl 2016-07-03 03:43:42 +02:00
8583a55424 Add TabPanel and TabButton user controls 2016-07-03 03:34:22 +02:00
3a7a0f63de Redo external link handling using ILifeSpanHandler instead of hacky JS code 2016-07-02 22:39:16 +02:00
2e13d08018 Remove unnecessary code from plugins.notification.js 2016-07-02 00:05:57 +02:00
f13515ba16 Add includeDisabled parameter to PluginManager.ExecutePlugins and update calls 2016-07-02 00:05:22 +02:00
6f1884451b Fix project file after separating plugins.js and breaking solution building 2016-07-02 00:04:36 +02:00
d9321a9acb Split plugins.js into separate browser and notification scripts and refactor notification script execution code 2016-07-01 23:53:08 +02:00
19f9614c74 Add ScriptLoader.GetRootIdentifier and PluginManager.HasAnyPlugin(environment) and use them 2016-07-01 23:47:06 +02:00
66ce355571 Update Plugins Form (change default size, store state, fix title) 2016-06-30 20:00:00 +02:00
5108649ec6 Fix WindowState saving and remove IsMaximized from config 2016-06-30 19:59:23 +02:00
9308478a59 Make WindowState.Restore firstTimeFullscreen argument required 2016-06-30 19:48:57 +02:00
97d778c31c Add a firstTimeFullscreen parameter to WindowState.Restore 2016-06-30 19:47:28 +02:00
740195146b Update FormBrowser and configuration to use WindowState 2016-06-30 19:43:38 +02:00
6e0717bf22 Add a WindowState class for easier Form saving and restoring 2016-06-30 19:39:49 +02:00
b6a683dfe1 Rewrite plugins.js, plugin state handling and script execution (separate instead of combining) 2016-06-30 16:31:02 +02:00
dc78c68f12 Change starting line in script processor to 1 instead of 0 for easier debugging 2016-06-30 15:49:14 +02:00
8e01d5ec84 Minor refactoring of ScriptLoader.ExecuteFile to generate identifier automatically 2016-06-30 15:34:54 +02:00
b531016c7b Refactor ScriptLoader for easier script identifier handling 2016-06-30 03:15:46 +02:00
188369e922 Implement optional minimum TweetDuck version tag for plugins 2016-06-26 23:18:46 +02:00
347c5054f9 Fix plugin load error text containing incorrect plugin names 2016-06-26 22:55:58 +02:00
6729b597c0 Restore program from tray if ran twice
Closes #39
2016-06-26 22:28:48 +02:00
7bdbfe937c Close modal dialogs with the back mouse button
Closes #37
2016-06-26 21:59:10 +02:00
d955713335 Finish plugin reloading and add plugin counts to the UI 2016-06-25 19:42:20 +02:00
8bb69ef0ee Rework plugin manager events (move to a different namespace and change LoadError to Load) 2016-06-25 19:38:20 +02:00
d258583a7f Disable page reload in design-revert plugin 2016-06-25 18:53:03 +02:00
13d0e10dcd Implement basic plugin loading and management 2016-06-25 18:53:03 +02:00
5557f79fe7 Fix image pasting in private message reply box
Closes #38
2016-06-22 17:53:13 +02:00
44ccaa0d26 Fix link expansion in notification popup
Closes #36
2016-06-22 17:42:13 +02:00
49f12abeb3 Add plugin author and website labels and adjust height based on description 2016-06-05 02:11:17 +02:00
b1859774bc Add a website entry to design-revert plugin metadata 2016-06-05 02:10:03 +02:00
4283403f0e Add a base for plugin management including basic UI 2016-06-04 23:24:46 +02:00
379191751c Move TweetDeck design reversion into a plugin for future 2016-06-03 15:06:01 +02:00
9bf396f2a8 Prepare plugin git handling and build event scripts 2016-06-03 15:04:56 +02:00
87af4c96c4 Reorganize resources in .csproj and move scripts to Resources\Scripts\ 2016-05-28 23:05:17 +02:00
dd78a7c9de Push .csproj file after the refactoring 2016-05-28 23:02:47 +02:00
40e534090d Refactor MigrationManager and FormBackgroundWork 2016-05-28 22:53:35 +02:00
973e0c489e Address code analysis issues 2016-05-28 22:34:40 +02:00
5eb64829a5 Release 1.2.3 2016-05-28 13:43:11 +02:00
5648e9e806 Add 'Copy quoted tweet address' to context menu
Closes #29
2016-05-27 14:30:11 +02:00
2a65e20fb9 Rewrite update handling and add an update check button to Settings 2016-05-26 14:13:30 +02:00
1162e9b655 Reorganize layout elements in Settings Form for easier modifications 2016-05-25 18:49:11 +02:00
b8a5c2fb9b Add a delay to link expansion and fix tooltip issues 2016-05-25 18:34:59 +02:00
eccd37a3eb Fix clicking on tweet links both opening browser and showing the tweet in app 2016-05-25 18:21:49 +02:00
ee282713f8 Fix program not closing when updating if 'Close to tray' is enabled
Closes #34
2016-05-22 15:12:45 +02:00
c5edad9c4b Add a button to clear cache to the Settings Form 2016-05-14 15:18:57 +02:00
293c01b12f Rewrite exit procedure to only run once and refactor 2016-05-13 16:25:22 +02:00
162bbc3221 Add option to disable HTML5 hardware acceleration
Closes #30
2016-05-13 14:01:11 +02:00
9f27020993 Release 1.2.2 2016-05-12 21:09:55 +02:00
c540cf45ca Push unsaved change that was supposed to be in previous commit 2016-05-12 18:21:04 +02:00
8bf4789646 Make updates search for .Update.exe file to allow for smaller and faster updates 2016-05-12 16:58:17 +02:00
fd97c20d37 Add second tray icon for unread tweets when notifications are muted
Closes #7
2016-05-12 15:46:39 +02:00
7238e17b86 Add link expanding and tooltip support to popup notifications 2016-05-12 15:10:22 +02:00
4c44da7f4a Refactor and optimize event handling in notification.js 2016-05-12 14:25:00 +02:00
af7657e3f8 Add EventTarget.addBubbledEventListener to notification.js for convenience 2016-05-11 17:54:07 +02:00
bea158b0d9 Fix example notification not running notification.js script (breaking links) 2016-05-11 17:40:41 +02:00
40acb49011 Add scrollbar to large popup notifications and display quoted tweets in the notification
Closes #9
2016-05-11 16:59:06 +02:00
cc7fc7e994 Resize notification Form based on font size 2016-05-10 18:21:56 +02:00
ddec715dda Add support for back/forward mouse buttons 2016-05-10 13:31:18 +02:00
a1365a98c0 Pause notification timer when context menu is open, and optimize Settings notification tick 2016-05-09 20:14:23 +02:00
9a29d4ff18 Add context menu item to copy tweet address without focusing date link
Closes #24
2016-05-09 20:00:36 +02:00
39816eae76 Fix case in 'Mute notifications' context menu item 2016-05-09 18:13:01 +02:00
45a38d9570 Add tooltip for collapsed links when expanding is disabled
Closes #25
2016-05-09 14:44:04 +02:00
a230258bec Remove FormBrowser.InvokeSafe and use extension method instead 2016-05-09 13:32:20 +02:00
8543205b85 Remove 'Copy' option from TweetDeck Migration and update lnk description 2016-05-08 21:45:57 +02:00
1d169005da Hide default icon in Forms that do not have custom one properly 2016-05-08 20:57:53 +02:00
f5212d88f3 Redesign the About screen 2016-05-08 20:53:08 +02:00
03a93ad3f3 Move native methods to a NativeMethods class 2016-05-08 18:33:42 +02:00
3a66fa28ab Do minor code refactoring 2016-05-08 14:59:01 +02:00
5f79b286f1 Fix t.co bypass not working in popup notifications 2016-05-08 14:48:11 +02:00
f6a4e39cfc Add 'Skip tweet' and 'Freeze' context menu options to notifications 2016-05-08 14:24:53 +02:00
24d8444043 Tweak context menu (fix and add separators) 2016-05-08 13:08:56 +02:00
0b8205dc41 Fix simultaneous popup notification order
Closes #26
2016-05-08 12:47:36 +02:00
98197d1e86 Fix right-aligned actions menu in Direct Messages
Closes #27
2016-05-08 12:16:41 +02:00
93 changed files with 4550 additions and 1200 deletions

1
.gitignore vendored
View File

@@ -47,7 +47,6 @@ artifacts/
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb

View File

@@ -5,9 +5,10 @@ using System.Threading;
namespace TweetDck.Configuration{
class LockManager{
public Process LockingProcess { get; private set; }
private readonly string file;
private FileStream lockStream;
private Process lockingProcess;
public LockManager(string file){
this.file = file;
@@ -25,9 +26,9 @@ namespace TweetDck.Configuration{
lockStream.Write(id,0,id.Length);
lockStream.Flush();
if (lockingProcess != null){
lockingProcess.Close();
lockingProcess = null;
if (LockingProcess != null){
LockingProcess.Close();
LockingProcess = null;
}
return true;
@@ -58,14 +59,12 @@ namespace TweetDck.Configuration{
using(Process currentProcess = Process.GetCurrentProcess()){
if (foundProcess.ProcessName == currentProcess.ProcessName){
lockingProcess = foundProcess;
LockingProcess = foundProcess;
}
currentProcess.Close();
}
}catch(ArgumentException){}
return lockingProcess == null && CreateLockFile();
return LockingProcess == null && CreateLockFile();
}catch(DirectoryNotFoundException){
string dir = Path.GetDirectoryName(file);
@@ -82,29 +81,39 @@ namespace TweetDck.Configuration{
return false;
}
public void Unlock(){
public bool Unlock(){
bool result = true;
if (lockStream != null){
lockStream.Dispose();
try{
File.Delete(file);
}catch(Exception e){
Program.Log(e.ToString());
result = false;
}
lockStream = null;
}
return result;
}
public bool CloseLockingProcess(int timeout){
if (lockingProcess != null){
lockingProcess.CloseMainWindow();
if (LockingProcess != null){
LockingProcess.CloseMainWindow();
for(int waited = 0; waited < timeout && !lockingProcess.HasExited;){
lockingProcess.Refresh();
for(int waited = 0; waited < timeout && !LockingProcess.HasExited;){
LockingProcess.Refresh();
Thread.Sleep(100);
waited += 100;
}
if (lockingProcess.HasExited){
lockingProcess.Dispose();
lockingProcess = null;
if (LockingProcess.HasExited){
LockingProcess.Dispose();
LockingProcess = null;
return true;
}
}

View File

@@ -1,28 +1,29 @@
using System;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using TweetDck.Core;
using TweetDck.Core.Handling;
using TweetDck.Core.Utils;
using TweetDck.Plugins;
namespace TweetDck.Configuration{
[Serializable]
sealed class UserConfig{
private static readonly IFormatter Formatter = new BinaryFormatter(){
private static readonly IFormatter Formatter = new BinaryFormatter{
Binder = new SerializationCompatibilityHandler()
};
private const int CurrentFileVersion = 2;
private const int CurrentFileVersion = 3;
// START OF CONFIGURATION
public bool IgnoreMigration { get; set; }
public bool IgnoreUninstallCheck { get; set; }
public bool IsMaximized { get; set; }
public Point WindowLocation { get; set; }
public Size WindowSize { get; set; }
public WindowState BrowserWindow { get; set; }
public bool DisplayNotificationTimer { get; set; }
public TweetNotification.Duration NotificationDuration { get; set; }
@@ -35,11 +36,8 @@ namespace TweetDck.Configuration{
public string DismissedUpdate { get; set; }
public bool ExpandLinksOnHover { get; set; }
public bool IsCustomWindowLocationSet{
get{
return WindowLocation.X != -32000 && WindowLocation.X != 32000;
}
}
public PluginConfig Plugins { get; private set; }
public WindowState PluginsWindow { get; set; }
public bool IsCustomNotificationPositionSet{
get{
@@ -97,15 +95,16 @@ namespace TweetDck.Configuration{
private UserConfig(string file){
this.file = file;
IsMaximized = true;
BrowserWindow = new WindowState();
DisplayNotificationTimer = true;
WindowLocation = new Point(-32000,-32000);
NotificationDuration = TweetNotification.Duration.Medium;
NotificationPosition = TweetNotification.Position.TopRight;
CustomNotificationPosition = new Point(-32000,-32000);
NotificationEdgeDistance = 8;
EnableUpdateCheck = true;
ExpandLinksOnHover = true;
Plugins = new PluginConfig();
PluginsWindow = new WindowState();
}
private void UpgradeFile(){
@@ -125,6 +124,13 @@ namespace TweetDck.Configuration{
++fileVersion;
}
if (fileVersion == 2){
BrowserWindow = new WindowState();
Plugins = new PluginConfig();
PluginsWindow = new WindowState();
++fileVersion;
}
// update the version
fileVersion = CurrentFileVersion;
Save();
@@ -179,7 +185,7 @@ namespace TweetDck.Configuration{
return config ?? new UserConfig(file);
}
private static string GetBackupFile(string file){
public static string GetBackupFile(string file){
return file+".bak";
}
@@ -192,7 +198,7 @@ namespace TweetDck.Configuration{
#endif
typeName = typeName.Replace("TweetDick","TweetDck");
return Type.GetType(string.Format("{0}, {1}",typeName,assemblyName));
return Type.GetType(string.Format(CultureInfo.CurrentCulture,"{0}, {1}",typeName,assemblyName));
}
}
}

View File

@@ -4,9 +4,9 @@ using System.Windows.Forms;
namespace TweetDck.Core.Controls{
static class ControlExtensions{
public static void InvokeSafe(this Form form, Action func){
if (form.InvokeRequired){
form.Invoke(func);
public static void InvokeSafe(this Control control, Action func){
if (control.InvokeRequired){
control.Invoke(func);
}
else{
func();

View File

@@ -1,33 +1,3 @@
namespace TweetDck.Core.Controls{
partial class FlatProgressBar {
/// <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 (brush != null)brush.Dispose();
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
components = new System.ComponentModel.Container();
}
#endregion
}
partial class FlatProgressBar{}
}

View File

@@ -23,5 +23,11 @@ namespace TweetDck.Core.Controls{
rect.Width = (int)(rect.Width*((double)Value/Maximum));
e.Graphics.FillRectangle(brush,rect);
}
protected override void Dispose(bool disposing){
if (brush != null)brush.Dispose();
base.Dispose(disposing);
}
}
}

View File

@@ -1,41 +0,0 @@
namespace TweetDck.Core.Controls {
partial class RichTextLabel {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// RichTextLabel
//
this.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.ReadOnly = true;
this.TabStop = false;
this.MouseEnter += new System.EventHandler(this.RichTextLabel_MouseEnter);
this.ResumeLayout(false);
}
#endregion
}
}

View File

@@ -1,58 +0,0 @@
using System;
using System.Windows.Forms;
namespace TweetDck.Core.Controls{
public partial class RichTextLabel : RichTextBox{
/// <summary>
/// Wraps the body of a RTF formatted string with default tags and formatting.
/// </summary>
public static string Wrap(string str){
string rtf = @"{\rtf1\ansi\ansicpg1250\deff0\deflang1029{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}";
rtf += @"{\*\generator Msftedit 4.20.69.1337;}\viewkind4\uc1\pard\sa200\sl276\slmult1\lang1036\f0\fs16 ";
rtf += str;
return rtf;
}
/// <summary>
/// Wraps URL tags around a link.
/// </summary>
public static string AddLink(string url){
return @"{\field{\*\fldinst{HYPERLINK """+url+@"""}}{\fldrslt{\ul\cf1 "+url+@"}}}";
}
/// <summary>
/// Uses v5 of RichTextBox, which fixes URLs and other crap.
/// </summary>
protected override CreateParams CreateParams{
get{
CreateParams createParams = base.CreateParams;
if (Program.LoadLibrary("msftedit.dll") != IntPtr.Zero){
createParams.ClassName = "RICHEDIT50W";
}
return createParams;
}
}
public RichTextLabel(){
InitializeComponent();
SetStyle(ControlStyles.Selectable,false);
SetStyle(ControlStyles.UserMouse,true);
SetStyle(ControlStyles.SupportsTransparentBackColor,true);
}
private void RichTextLabel_MouseEnter(object sender, EventArgs e){
Cursor = Cursors.Default;
}
protected override void WndProc(ref Message m){
if (m.Msg == 0x204 || m.Msg == 0x205){ // WM_RBUTTONDOWN, WM_RBUTTONUP
return;
}
base.WndProc(ref m);
}
}
}

3
Core/Controls/TabButton.Designer.cs generated Normal file
View File

@@ -0,0 +1,3 @@
namespace TweetDck.Core.Controls{
partial class TabButton{}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TweetDck.Core.Controls{
sealed partial class TabButton : Button{
protected override bool ShowFocusCues{
get{
return false;
}
}
public Action Callback { get; private set; }
public TabButton(){
GotFocus += TabButton_GotFocus;
}
public void SetupButton(int locationX, int sizeWidth, string title, Action callback){
Callback = callback;
SuspendLayout();
FlatAppearance.BorderColor = Color.DimGray;
FlatAppearance.MouseDownBackColor = Color.White;
FlatAppearance.MouseOverBackColor = Color.White;
FlatStyle = FlatStyle.Flat;
Location = new Point(locationX,0);
Margin = new Padding(0);
Size = new Size(sizeWidth,30);
Text = title;
UseVisualStyleBackColor = true;
ResumeLayout(true);
}
private void TabButton_GotFocus(object sender, EventArgs e){ // removes extra border when focused
NotifyDefault(false);
}
}
}

68
Core/Controls/TabPanel.Designer.cs generated Normal file
View File

@@ -0,0 +1,68 @@
namespace TweetDck.Core.Controls {
partial class TabPanel {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.panelButtons = new System.Windows.Forms.Panel();
this.panelContent = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// panelButtons
//
this.panelButtons.Dock = System.Windows.Forms.DockStyle.Top;
this.panelButtons.Location = new System.Drawing.Point(0, 0);
this.panelButtons.Margin = new System.Windows.Forms.Padding(0);
this.panelButtons.Name = "panelButtons";
this.panelButtons.Size = new System.Drawing.Size(640, 30);
this.panelButtons.TabIndex = 0;
//
// panelContent
//
this.panelContent.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.panelContent.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.panelContent.Location = new System.Drawing.Point(0, 29);
this.panelContent.Margin = new System.Windows.Forms.Padding(0);
this.panelContent.Name = "panelContent";
this.panelContent.Size = new System.Drawing.Size(640, 451);
this.panelContent.TabIndex = 1;
//
// TabPanel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.panelContent);
this.Controls.Add(this.panelButtons);
this.Name = "TabPanel";
this.Size = new System.Drawing.Size(640, 480);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panelButtons;
private System.Windows.Forms.Panel panelContent;
}
}

62
Core/Controls/TabPanel.cs Normal file
View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace TweetDck.Core.Controls{
sealed partial class TabPanel : UserControl{
public IEnumerable<TabButton> Buttons{
get{
return panelButtons.Controls.Cast<TabButton>();
}
}
public TabButton ActiveButton { get; private set; }
// ReSharper disable once ConvertToAutoPropertyWithPrivateSetter
public Panel Content{
get{
return panelContent;
}
}
private int btnWidth;
public TabPanel(){
InitializeComponent();
}
public void SetupTabPanel(int buttonWidth){
this.btnWidth = buttonWidth;
}
public TabButton AddButton(string title, Action callback){
TabButton button = new TabButton();
button.SetupButton((btnWidth-1)*panelButtons.Controls.Count,btnWidth,title,callback);
button.Click += (sender, args) => SelectTab((TabButton)sender);
panelButtons.Controls.Add(button);
return button;
}
public void SelectTab(TabButton button){
if (ActiveButton != null){
ActiveButton.BackColor = SystemColors.Control;
}
button.BackColor = Color.White;
button.Callback();
ActiveButton = button;
}
public void ReplaceContent(Control newControl){
newControl.Dock = DockStyle.Fill;
Content.SuspendLayout();
Content.Controls.Clear();
Content.Controls.Add(newControl);
Content.ResumeLayout(true);
}
}
}

View File

@@ -23,8 +23,10 @@
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormBrowser));
this.trayIcon = new TweetDck.Core.TrayIcon();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// trayIcon
@@ -51,6 +53,7 @@
#endregion
private TrayIcon trayIcon;
private System.Windows.Forms.ToolTip toolTip;
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
@@ -7,8 +6,11 @@ using TweetDck.Configuration;
using TweetDck.Core.Handling;
using TweetDck.Core.Other;
using TweetDck.Resources;
using TweetDck.Core.Utils;
using TweetDck.Core.Controls;
using System.Drawing;
using TweetDck.Updates;
using TweetDck.Plugins;
using TweetDck.Plugins.Events;
namespace TweetDck.Core{
sealed partial class FormBrowser : Form{
@@ -21,43 +23,51 @@ namespace TweetDck.Core{
public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser;
private readonly TweetDeckBridge bridge;
private readonly FormNotification notification;
private readonly PluginManager plugins;
private readonly UpdateHandler updates;
private FormSettings currentFormSettings;
private FormAbout currentFormAbout;
private FormPlugins currentFormPlugins;
private bool isLoaded;
private FormWindowState prevState;
public FormBrowser(){
public FormBrowser(PluginManager pluginManager){
InitializeComponent();
Text = Program.BrandName;
bridge = new TweetDeckBridge(this);
this.plugins = pluginManager;
this.plugins.Reloaded += plugins_Reloaded;
this.plugins.Config.PluginChangedState += plugins_PluginChangedState;
browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
FormNotification notification = CreateNotificationForm(true);
notification.CanMoveWindow = () => false;
notification.Show();
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
MenuHandler = new ContextMenuBrowser(this),
DialogHandler = new DialogHandlerBrowser(this)
DialogHandler = new DialogHandlerBrowser(this),
LifeSpanHandler = new LifeSpanHandler()
};
browser.LoadingStateChanged += Browser_LoadingStateChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
browser.RegisterJsObject("$TD",bridge);
this.browser.LoadingStateChanged += Browser_LoadingStateChanged;
this.browser.FrameLoadEnd += Browser_FrameLoadEnd;
this.browser.RegisterJsObject("$TD",new TweetDeckBridge(this,notification));
Controls.Add(browser);
Disposed += (sender, args) => browser.Dispose();
trayIcon.ClickRestore += trayIcon_ClickRestore;
trayIcon.ClickClose += trayIcon_ClickClose;
this.trayIcon.ClickRestore += trayIcon_ClickRestore;
this.trayIcon.ClickClose += trayIcon_ClickClose;
Config.TrayBehaviorChanged += Config_TrayBehaviorChanged;
UpdateTrayIcon();
notification = new FormNotification(this,bridge,true){ CanMoveWindow = () => false };
notification.Show();
this.updates = new UpdateHandler(browser,this);
this.updates.UpdateAccepted += updates_UpdateAccepted;
}
private void ShowChildForm(Form form){
@@ -65,21 +75,19 @@ namespace TweetDck.Core{
form.MoveToCenter(this);
}
private void ForceClose(){
trayIcon.Visible = false; // checked in FormClosing event
Close();
}
public FormNotification CreateNotificationForm(bool autoHide){
return new FormNotification(this,plugins,trayIcon,autoHide);
}
// window setup
private void SetupWindow(){
if (Config.IsCustomWindowLocationSet){
Location = Config.WindowLocation;
Size = Config.WindowSize;
WindowState = Config.IsMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
if (!Config.IsCustomWindowLocationSet || !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(Bounds))){
Location = Screen.PrimaryScreen.WorkingArea.Location;
Size = Screen.PrimaryScreen.WorkingArea.Size;
WindowState = FormWindowState.Maximized;
}
Config.BrowserWindow.Restore(this,true);
prevState = WindowState;
isLoaded = true;
}
@@ -99,8 +107,11 @@ namespace TweetDck.Core{
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain){
foreach(string js in ScriptLoader.LoadResources("code.js","update.js").Where(js => js != null)){
browser.ExecuteScriptAsync(js);
ScriptLoader.ExecuteFile(e.Frame,"code.js");
if (plugins.HasAnyPlugin(PluginEnvironment.Browser)){
ScriptLoader.ExecuteFile(e.Frame,PluginManager.PluginBrowserScriptFile);
plugins.ExecutePlugins(e.Frame,PluginEnvironment.Browser,true);
}
}
}
@@ -126,13 +137,7 @@ namespace TweetDck.Core{
if (!isLoaded)return;
if (Location.X != -32000){
Config.IsMaximized = WindowState == FormWindowState.Maximized;
if (WindowState == FormWindowState.Normal || (WindowState == FormWindowState.Maximized && !Screen.FromControl(this).Equals(Screen.FromPoint(Config.WindowLocation)))){
Config.WindowLocation = Location;
Config.WindowSize = Size;
}
Config.BrowserWindow.Save(this);
Config.Save();
}
}
@@ -165,16 +170,52 @@ namespace TweetDck.Core{
private void trayIcon_ClickClose(object sender, EventArgs e){
if (!isLoaded)return;
trayIcon.Visible = false; // checked in FormClosing event
Close();
ForceClose();
}
private void plugins_Reloaded(object sender, PluginLoadEventArgs e){
browser.ExecuteScriptAsync("window.location.reload()");
}
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("window.TDPF_setPluginState",e.Plugin,e.IsEnabled ? 1 : 0); // ExecuteScriptAsync cannot handle boolean values as of yet
}
private void updates_UpdateAccepted(object sender, UpdateAcceptedEventArgs e){
Hide();
FormUpdateDownload downloadForm = new FormUpdateDownload(e.UpdateInfo);
downloadForm.MoveToCenter(this);
downloadForm.ShowDialog();
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
UpdateInstallerPath = downloadForm.InstallerPath;
ForceClose();
}
else if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Manual){
ForceClose();
}
else{
Show();
}
}
protected override void WndProc(ref Message m){
if (isLoaded && m.Msg == Program.WindowRestoreMessage){
trayIcon_ClickRestore(trayIcon,new EventArgs());
return;
}
if (isLoaded && m.Msg == 0x210 && (m.WParam.ToInt32() & 0xFFFF) == 0x020B){ // WM_PARENTNOTIFY, WM_XBUTTONDOWN
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra",(m.WParam.ToInt32() >> 16) & 0xFFFF);
return;
}
base.WndProc(ref m);
}
// callback handlers
public void InvokeSafe(Action func){
ControlExtensions.InvokeSafe(this,func);
}
public void OpenSettings(){
if (currentFormSettings != null){
currentFormSettings.BringToFront();
@@ -182,7 +223,7 @@ namespace TweetDck.Core{
else{
bool prevEnableUpdateCheck = Config.EnableUpdateCheck;
currentFormSettings = new FormSettings(this);
currentFormSettings = new FormSettings(this,updates);
currentFormSettings.FormClosed += (sender, args) => {
currentFormSettings = null;
@@ -190,8 +231,7 @@ namespace TweetDck.Core{
if (!prevEnableUpdateCheck && Config.EnableUpdateCheck){
Config.DismissedUpdate = string.Empty;
Config.Save();
browser.ExecuteScriptAsync("TDGF_runUpdateCheck",new object[0]);
updates.Check(false);
}
};
@@ -210,14 +250,32 @@ namespace TweetDck.Core{
}
}
public void OnTweetPopup(TweetNotification tweet){
notification.ShowNotification(tweet);
public void OpenPlugins(){
if (currentFormPlugins != null){
currentFormPlugins.BringToFront();
}
else{
currentFormPlugins = new FormPlugins(plugins);
currentFormPlugins.FormClosed += (sender, args) => currentFormPlugins = null;
ShowChildForm(currentFormPlugins);
}
}
public void OnTweetSound(){
}
public void DisplayTooltip(string text){
if (string.IsNullOrEmpty(text)){
toolTip.Hide(this);
}
else{
Point position = PointToClient(Cursor.Position);
position.Offset(20,10);
toolTip.Show(text,this,position);
}
}
public void OnImagePasted(){
browser.ExecuteScriptAsync("TDGF_tryPasteImage",new object[0]);
}
@@ -225,24 +283,5 @@ namespace TweetDck.Core{
public void OnImagePastedFinish(){
browser.ExecuteScriptAsync("TDGF_tryPasteImageFinish",new object[0]);
}
public void BeginUpdateProcess(string versionTag, string downloadUrl){
Hide();
FormUpdateDownload downloadForm = new FormUpdateDownload(new UpdateInfo(versionTag,downloadUrl));
downloadForm.MoveToCenter(this);
downloadForm.ShowDialog();
if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Succeeded){
UpdateInstallerPath = downloadForm.InstallerPath;
Close();
}
else if (downloadForm.UpdateStatus == FormUpdateDownload.Status.Manual){
Close();
}
else{
Show();
}
}
}
}

View File

@@ -120,6 +120,9 @@
<metadata name="trayIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>112, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@@ -29,6 +29,7 @@ namespace TweetDck.Core {
this.panelBrowser = new System.Windows.Forms.Panel();
this.timerProgress = new System.Windows.Forms.Timer(this.components);
this.progressBarTimer = new TweetDck.Core.Controls.FlatProgressBar();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.SuspendLayout();
//
// panelBrowser
@@ -49,6 +50,8 @@ namespace TweetDck.Core {
//
// progressBarTimer
//
this.progressBarTimer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBarTimer.BackColor = System.Drawing.SystemColors.Control;
this.progressBarTimer.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(28)))), ((int)(((byte)(99)))), ((int)(((byte)(153)))));
this.progressBarTimer.Location = new System.Drawing.Point(0, 118);
@@ -71,6 +74,7 @@ namespace TweetDck.Core {
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormNotification";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormNotification_FormClosing);
@@ -83,5 +87,6 @@ namespace TweetDck.Core {
private System.Windows.Forms.Panel panelBrowser;
private Controls.FlatProgressBar progressBarTimer;
private System.Windows.Forms.Timer timerProgress;
private System.Windows.Forms.ToolTip toolTip;
}
}

View File

@@ -7,19 +7,49 @@ using CefSharp.WinForms;
using TweetDck.Configuration;
using TweetDck.Core.Handling;
using TweetDck.Resources;
using TweetDck.Core.Utils;
using TweetDck.Plugins;
namespace TweetDck.Core{
sealed partial class FormNotification : Form{
private const string NotificationScriptFile = "notification.js";
private static readonly string NotificationScriptIdentifier = ScriptLoader.GetRootIdentifier(NotificationScriptFile);
private static readonly string PluginScriptIdentifier = ScriptLoader.GetRootIdentifier(PluginManager.PluginNotificationScriptFile);
public Func<bool> CanMoveWindow = () => true;
private readonly Form owner;
private readonly PluginManager plugins;
private readonly TrayIcon trayIcon;
private readonly ChromiumWebBrowser browser;
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
private readonly bool autoHide;
private int timeLeft, totalTime;
private bool? prevDisplayTimer;
private int? prevFontSize;
private bool RequiresResize{
get{
return !prevDisplayTimer.HasValue || !prevFontSize.HasValue || prevDisplayTimer != Program.UserConfig.DisplayNotificationTimer || prevFontSize != TweetNotification.FontSizeLevel;
}
set{
if (value){
prevDisplayTimer = null;
prevFontSize = null;
}
else{
prevDisplayTimer = Program.UserConfig.DisplayNotificationTimer;
prevFontSize = TweetNotification.FontSizeLevel;
}
}
}
private readonly string notificationJS;
private readonly string pluginJS;
protected override bool ShowWithoutActivation{
get{
@@ -27,24 +57,50 @@ namespace TweetDck.Core{
}
}
public FormNotification(Form owner, TweetDeckBridge bridge, bool autoHide){
public bool FreezeTimer { get; set; }
public bool ContextMenuOpen { get; set; }
public string CurrentUrl { get; private set; }
public EventHandler Initialized;
private bool isInitialized;
private static int BaseClientWidth{
get{
int level = TweetNotification.FontSizeLevel;
return level == 0 ? 284 : (int)Math.Round(284.0*(1.0+0.05*level));
}
}
private static int BaseClientHeight{
get{
int level = TweetNotification.FontSizeLevel;
return level == 0 ? 118 : (int)Math.Round(118.0*(1.0+0.075*level));
}
}
public FormNotification(FormBrowser owner, PluginManager plugins, TrayIcon trayIcon, bool autoHide){
InitializeComponent();
Text = Program.BrandName;
this.owner = owner;
this.plugins = plugins;
this.trayIcon = trayIcon;
this.autoHide = autoHide;
owner.FormClosed += (sender, args) => Close();
notificationJS = ScriptLoader.LoadResource("notification.js");
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
browser = new ChromiumWebBrowser("about:blank"){ MenuHandler = new ContextMenuNotification() };
browser = new ChromiumWebBrowser("about:blank"){
MenuHandler = new ContextMenuNotification(this,autoHide),
LifeSpanHandler = new LifeSpanHandler()
};
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
browser.FrameLoadEnd += Browser_FrameLoadEnd;
if (bridge != null){
browser.RegisterJsObject("$TD",bridge);
}
browser.RegisterJsObject("$TD",new TweetDeckBridge(owner,this));
panelBrowser.Controls.Add(browser);
@@ -56,8 +112,6 @@ namespace TweetDck.Core{
Disposed += (sender, args) => browser.Dispose();
}
public FormNotification(Form owner, bool autoHide) : this(owner,null,autoHide){}
protected override void WndProc(ref Message m){
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
return;
@@ -69,42 +123,58 @@ namespace TweetDck.Core{
// event handlers
private void timerHideProgress_Tick(object sender, EventArgs e){
if (Bounds.Contains(Cursor.Position))return;
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
timeLeft -= timerProgress.Interval;
progressBarTimer.SetValueInstant((int)Math.Min(1000,Math.Round(1050.0*(totalTime-timeLeft)/totalTime)));
progressBarTimer.SetValueInstant((int)Math.Min(1000,Math.Round(1025.0*(totalTime-timeLeft)/totalTime)));
if (timeLeft <= 0){
if (tweetQueue.Count > 0){
LoadNextNotification();
}
else if (autoHide){
HideNotification();
}
FinishCurrentTweet();
}
}
private void Config_MuteToggled(object sender, EventArgs e){
if (Program.UserConfig.MuteNotifications){
HideNotification();
HideNotification(true);
}
else{
if (tweetQueue.Count > 0){
MoveToVisibleLocation();
LoadNextNotification();
}
trayIcon.HasNotifications = false;
}
}
private void Browser_IsBrowserInitializedChanged(object sender, IsBrowserInitializedChangedEventArgs e){
if (e.IsBrowserInitialized && Initialized != null){
Initialized(this,new EventArgs());
}
}
private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && notificationJS != null && browser.Address != "about:blank"){
browser.ExecuteScriptAsync(notificationJS);
if (!e.Frame.IsMain)return;
if (!isInitialized){
isInitialized = true;
if (Initialized != null){
Initialized(this,new EventArgs());
}
}
else if (notificationJS != null && browser.Address != "about:blank"){
ScriptLoader.ExecuteScript(e.Frame,notificationJS,NotificationScriptIdentifier);
if (plugins.HasAnyPlugin(PluginEnvironment.Notification)){
ScriptLoader.ExecuteScript(e.Frame,pluginJS,PluginScriptIdentifier);
plugins.ExecutePlugins(e.Frame,PluginEnvironment.Notification,false);
}
}
}
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
if (e.CloseReason == CloseReason.UserClosing){
HideNotification();
HideNotification(false);
tweetQueue.Clear();
e.Cancel = true;
}
@@ -115,10 +185,9 @@ namespace TweetDck.Core{
public void ShowNotification(TweetNotification notification){
if (Program.UserConfig.MuteNotifications){
tweetQueue.Enqueue(notification);
trayIcon.HasNotifications = true;
}
else{
MoveToVisibleLocation();
tweetQueue.Enqueue(notification);
UpdateTitle();
@@ -129,62 +198,81 @@ namespace TweetDck.Core{
}
public void ShowNotificationForSettings(bool reset){
if (browser.Address == "about:blank"){
browser.Load("about:blank"); // required, otherwise shit breaks
reset = true;
if (reset){
LoadTweet(TweetNotification.ExampleTweet);
}
else{
MoveToVisibleLocation();
}
}
if (reset){
browser.LoadHtml(TweetNotification.ExampleTweet.GenerateHtml(),"http://tweetdeck.twitter.com/");
public void HideNotification(bool loadBlank){
if (loadBlank){
browser.LoadHtml("","about:blank");
}
totalTime = timeLeft = TweetNotification.ExampleTweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
Location = new Point(-32000,-32000);
progressBarTimer.Value = 0;
timerProgress.Stop();
}
public void OnNotificationReady(){
UpdateTitle();
MoveToVisibleLocation();
timerProgress.Start();
}
MoveToVisibleLocation();
public void FinishCurrentTweet(){
if (tweetQueue.Count > 0){
LoadNextNotification();
}
public void HideNotification(){
browser.LoadHtml("","about:blank");
Location = new Point(-32000,-32000);
else if (autoHide){
HideNotification(true);
}
else{
timerProgress.Stop();
}
}
private void LoadNextNotification(){
TweetNotification tweet = tweetQueue.Dequeue();
if (browser.Address == "about:blank"){
browser.Load("about:blank"); // required, otherwise shit breaks
LoadTweet(tweetQueue.Dequeue());
}
browser.LoadHtml(tweet.GenerateHtml(),"http://tweetdeck.twitter.com/");
private void LoadTweet(TweetNotification tweet){
CurrentUrl = tweet.Url;
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
timerProgress.Stop();
timerProgress.Start();
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDuration);
progressBarTimer.Value = 0;
UpdateTitle();
browser.LoadHtml(tweet.GenerateHtml(),"http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
}
private void MoveToVisibleLocation(){
bool needsReactivating = Location.X == -32000;
UserConfig config = Program.UserConfig;
Screen screen = Screen.FromControl(owner);
if (RequiresResize){
RequiresResize = false;
if (config.DisplayNotificationTimer){
ClientSize = new Size(ClientSize.Width,122);
ClientSize = new Size(BaseClientWidth,BaseClientHeight+4);
progressBarTimer.Visible = true;
}
else{
ClientSize = new Size(ClientSize.Width,118);
ClientSize = new Size(BaseClientWidth,BaseClientHeight);
progressBarTimer.Visible = false;
}
panelBrowser.Height = BaseClientHeight;
}
Screen screen = Screen.FromControl(owner);
if (config.NotificationDisplay > 0 && config.NotificationDisplay <= Screen.AllScreens.Length){
screen = Screen.AllScreens[config.NotificationDisplay-1];
}
bool needsReactivating = Location.X == -32000;
int edgeDist = config.NotificationEdgeDistance;
switch(config.NotificationPosition){
@@ -215,12 +303,23 @@ namespace TweetDck.Core{
}
if (needsReactivating){
Program.SetWindowPos(Handle.ToInt32(),-1,Left,Top,Width,Height,0x0010); // HWND_TOPMOST, SWP_NOACTIVATE
NativeMethods.SetFormPos(this,NativeMethods.HWND_TOPMOST,NativeMethods.SWP_NOACTIVATE);
}
}
private void UpdateTitle(){
Text = tweetQueue.Count > 0 ? Program.BrandName+" ("+tweetQueue.Count+" more left)" : Program.BrandName;
}
public void DisplayTooltip(string text){
if (string.IsNullOrEmpty(text)){
toolTip.Hide(this);
}
else{
Point position = PointToClient(Cursor.Position);
position.Offset(20,5);
toolTip.Show(text,this,position);
}
}
}
}

View File

@@ -1,4 +1,5 @@
using CefSharp;
using System;
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Utils;
@@ -12,9 +13,7 @@ namespace TweetDck.Core.Handling{
private const int MenuCopyImageUrl = 26504;
public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
RemoveSeparatorIfLast(model);
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#")){
if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#",StringComparison.Ordinal)){
model.AddItem((CefMenuCommand)MenuOpenUrlInBrowser,"Open in browser");
model.AddItem((CefMenuCommand)MenuCopyUrl,"Copy link address");
model.AddSeparator();
@@ -79,13 +78,7 @@ namespace TweetDck.Core.Handling{
return false;
}
protected void RemoveSeparatorIfFirst(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);
}
}
protected void RemoveSeparatorIfLast(IMenuModel model){
protected static void RemoveSeparatorIfLast(IMenuModel model){
if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){
model.RemoveAt(model.Count-1);
}

View File

@@ -1,10 +1,15 @@
using CefSharp;
using System.Windows.Forms;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Handling{
class ContextMenuBrowser : ContextMenuBase{
private const int MenuSettings = 26600;
private const int MenuPlugins = 26005;
private const int MenuAbout = 26601;
private const int MenuMute = 26602;
private const int MenuCopyTweetUrl = 26603;
private const int MenuCopyTweetEmbeddedUrl = 26604;
private readonly FormBrowser form;
@@ -17,20 +22,32 @@ namespace TweetDck.Core.Handling{
model.Remove(CefMenuCommand.Forward);
model.Remove(CefMenuCommand.Print);
model.Remove(CefMenuCommand.ViewSource);
RemoveSeparatorIfLast(model);
RemoveSeparatorIfFirst(model);
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweet)){
model.AddItem((CefMenuCommand)MenuCopyTweetUrl,"Copy tweet address");
if (!string.IsNullOrEmpty(TweetDeckBridge.LastHighlightedTweetEmbedded)){
model.AddItem((CefMenuCommand)MenuCopyTweetEmbeddedUrl,"Copy quoted tweet address");
}
model.AddSeparator();
}
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
if (model.Count > 0){
RemoveSeparatorIfLast(model);
model.AddSeparator();
}
model.AddItem(CefMenuCommand.Reload,"Reload");
model.AddCheckItem((CefMenuCommand)MenuMute,"Mute Notifications");
model.AddCheckItem((CefMenuCommand)MenuMute,"Mute notifications");
model.SetChecked((CefMenuCommand)MenuMute,Program.UserConfig.MuteNotifications);
model.AddSeparator();
if (TweetNotification.IsReady){
model.AddItem((CefMenuCommand)MenuSettings,"Settings");
}
model.AddItem((CefMenuCommand)MenuPlugins,"Plugins");
model.AddItem((CefMenuCommand)MenuAbout,"About "+Program.BrandName);
}
@@ -41,17 +58,15 @@ namespace TweetDck.Core.Handling{
switch((int)commandId){
case MenuSettings:
form.InvokeSafe(() => {
form.OpenSettings();
});
form.InvokeSafe(form.OpenSettings);
return true;
case MenuAbout:
form.InvokeSafe(() => {
form.OpenAbout();
});
form.InvokeSafe(form.OpenAbout);
return true;
case MenuPlugins:
form.InvokeSafe(form.OpenPlugins);
return true;
case MenuMute:
@@ -61,6 +76,14 @@ namespace TweetDck.Core.Handling{
});
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweet,TextDataFormat.UnicodeText);
return true;
case MenuCopyTweetEmbeddedUrl:
Clipboard.SetText(TweetDeckBridge.LastHighlightedTweetEmbedded,TextDataFormat.UnicodeText);
return true;
}
return false;

View File

@@ -1,12 +1,77 @@
using CefSharp;
using System.Windows.Forms;
using CefSharp;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Handling{
class ContextMenuNotification : ContextMenuBase{
private const int MenuSkipTweet = 26600;
private const int MenuFreeze = 26601;
private const int MenuCopyTweetUrl = 26602;
private const int MenuCopyTweetEmbeddedUrl = 26603;
private readonly FormNotification form;
private readonly bool enableCustomMenu;
public ContextMenuNotification(FormNotification form, bool enableCustomMenu){
this.form = form;
this.enableCustomMenu = enableCustomMenu;
}
public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
model.Clear();
if (enableCustomMenu){
model.AddItem((CefMenuCommand)MenuSkipTweet,"Skip tweet");
model.AddCheckItem((CefMenuCommand)MenuFreeze,"Freeze");
model.SetChecked((CefMenuCommand)MenuFreeze,form.FreezeTimer);
model.AddSeparator();
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");
}
model.AddSeparator();
}
}
base.OnBeforeContextMenu(browserControl,browser,frame,parameters,model);
RemoveSeparatorIfLast(model);
form.InvokeSafe(() => form.ContextMenuOpen = true);
}
public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){
if (base.OnContextMenuCommand(browserControl,browser,frame,parameters,commandId,eventFlags)){
return true;
}
switch((int)commandId){
case MenuSkipTweet:
form.InvokeSafe(form.FinishCurrentTweet);
return true;
case MenuFreeze:
form.InvokeSafe(() => form.FreezeTimer = !form.FreezeTimer);
return true;
case MenuCopyTweetUrl:
Clipboard.SetText(form.CurrentUrl,TextDataFormat.UnicodeText);
return true;
case MenuCopyTweetEmbeddedUrl:
Clipboard.SetText(TweetDeckBridge.NotificationTweetEmbedded,TextDataFormat.UnicodeText);
return true;
}
return false;
}
public override void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
base.OnContextMenuDismissed(browserControl,browser,frame);
form.InvokeSafe(() => form.ContextMenuOpen = false);
}
}
}

View File

@@ -1,5 +1,6 @@
using CefSharp;
using System.Collections.Generic;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Handling{
class DialogHandlerBrowser : IDialogHandler{

View File

@@ -0,0 +1,30 @@
using CefSharp;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Handling{
class LifeSpanHandler : ILifeSpanHandler{
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser){
newBrowser = null;
switch(targetDisposition){
case WindowOpenDisposition.NewBackgroundTab:
case WindowOpenDisposition.NewForegroundTab:
case WindowOpenDisposition.NewPopup:
case WindowOpenDisposition.NewWindow:
BrowserUtils.OpenExternalBrowser(targetUrl);
return true;
default:
return false;
}
}
public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser){}
public bool DoClose(IWebBrowser browserControl, IBrowser browser){
return false;
}
public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser){}
}
}

View File

@@ -4,13 +4,18 @@ using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
using TweetDck.Core.Utils;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Handling{
class TweetDeckBridge{
public static string LastRightClickedLink = string.Empty;
public static string LastHighlightedTweet = string.Empty;
public static string LastHighlightedTweetEmbedded = string.Empty;
public static string NotificationTweetEmbedded = string.Empty;
public static string ClipboardImagePath = string.Empty;
private readonly FormBrowser form;
private readonly FormNotification notification;
public string BrandName{
get{
@@ -30,26 +35,15 @@ namespace TweetDck.Core.Handling{
}
}
public bool UpdateCheckEnabled{
get{
return Program.UserConfig.EnableUpdateCheck;
}
}
public bool ExpandLinksOnHover{
get{
return Program.UserConfig.ExpandLinksOnHover;
}
}
public string DismissedVersionTag{
get{
return Program.UserConfig.DismissedUpdate ?? string.Empty;
}
}
public TweetDeckBridge(FormBrowser form){
public TweetDeckBridge(FormBrowser form, FormNotification notification){
this.form = form;
this.notification = notification;
}
public void LoadFontSizeClass(string fsClass){
@@ -65,40 +59,49 @@ namespace TweetDck.Core.Handling{
}
public void SetLastRightClickedLink(string link){
form.InvokeSafe(() => LastRightClickedLink = link);
}
public void SetLastHighlightedTweet(string link, string embeddedLink){
form.InvokeSafe(() => {
LastRightClickedLink = link;
LastHighlightedTweet = link;
LastHighlightedTweetEmbedded = embeddedLink;
});
}
public void SetNotificationTweetEmbedded(string link){
form.InvokeSafe(() => NotificationTweetEmbedded = link);
}
public void OpenSettingsMenu(){
form.InvokeSafe(() => {
form.OpenSettings();
});
form.InvokeSafe(form.OpenSettings);
}
public void OnTweetPopup(string tweetHtml, int tweetCharacters){
form.InvokeSafe(() => {
form.OnTweetPopup(new TweetNotification(tweetHtml,tweetCharacters));
public void OpenPluginsMenu(){
form.InvokeSafe(form.OpenPlugins);
}
public void OnTweetPopup(string tweetHtml, string tweetUrl, int tweetCharacters){
notification.InvokeSafe(() => {
notification.ShowNotification(new TweetNotification(tweetHtml,tweetUrl,tweetCharacters));
});
}
public void OnTweetSound(){
form.InvokeSafe(() => {
form.OnTweetSound();
});
form.InvokeSafe(form.OnTweetSound);
}
public void OnUpdateAccepted(string versionTag, string downloadUrl){
form.InvokeSafe(() => {
form.BeginUpdateProcess(versionTag,downloadUrl);
});
public void OnNotificationReady(){
notification.InvokeSafe(notification.OnNotificationReady);
}
public void OnUpdateDismissed(string versionTag){
form.InvokeSafe(() => {
Program.UserConfig.DismissedUpdate = versionTag;
Program.UserConfig.Save();
});
public void DisplayTooltip(string text, bool showInNotification){
if (showInNotification){
notification.InvokeSafe(() => notification.DisplayTooltip(text));
}
else{
form.InvokeSafe(() => form.DisplayTooltip(text));
}
}
public void TryPasteImage(){
@@ -126,10 +129,9 @@ namespace TweetDck.Core.Handling{
Point prevPos = Cursor.Position;
Cursor.Position = form.PointToScreen(new Point(offsetX,offsetY));
Program.mouse_event(SystemInformation.MouseButtonsSwapped ? 0x08 : 0x02,Cursor.Position.X,Cursor.Position.Y,0,0); // MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_LEFTDOWN
Program.mouse_event(SystemInformation.MouseButtonsSwapped ? 0x10 : 0x04,Cursor.Position.X,Cursor.Position.Y,0,0); // MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_LEFTUP
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
Cursor.Position = prevPos;
form.OnImagePastedFinish();
});
}

View File

@@ -6,9 +6,27 @@ namespace TweetDck.Core.Handling{
private static string FontSizeClass { get; set; }
private static string HeadTag { get; set; }
public static bool IsReady{
private static string DefaultFontSizeClass{
get{
return FontSizeClass != null && HeadTag != null;
return "medium";
}
}
private static string DefaultHeadTag{
get{
return @"<meta charset='utf-8'><meta http-equiv='X-UA-Compatible' content='chrome=1'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/font.5ef884f9f9.css'><link rel='stylesheet' href='https://ton.twimg.com/tweetdeck-web/web/css/app-dark.5631e0dd42.css'>";
}
}
public static int FontSizeLevel{
get{
switch(FontSizeClass){
case "largest": return 4;
case "large": return 3;
case "medium": return 2;
case "small": return 1;
default: return 0;
}
}
}
@@ -26,7 +44,7 @@ namespace TweetDck.Core.Handling{
build.Append(@"<div class='tweet-body'><p class='js-tweet-text tweet-text with-linebreaks'>This is an example tweet, which lets you test the location and duration of popup notifications.</p></div>");
build.Append(@"</div></div></article>");
return new TweetNotification(build.ToString(),95);
return new TweetNotification(build.ToString(),"",95);
}
}
@@ -46,11 +64,19 @@ namespace TweetDck.Core.Handling{
Short, Medium, Long, VeryLong
}
public string Url{
get{
return url;
}
}
private readonly string html;
private readonly string url;
private readonly int characters;
public TweetNotification(string html, int characters){
public TweetNotification(string html, string url, int characters){
this.html = html;
this.url = url;
this.characters = characters;
}
@@ -70,9 +96,9 @@ namespace TweetDck.Core.Handling{
public string GenerateHtml(){
StringBuilder build = new StringBuilder();
build.Append("<!DOCTYPE html>");
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass).Append("'>");
build.Append("<head>").Append(HeadTag).Append("</head>");
build.Append("<body class='hearty'><div class='app-columns-container'><div class='column' style='width:100%'>");
build.Append("<html class='os-windows txt-base-").Append(FontSizeClass ?? DefaultFontSizeClass).Append("'>");
build.Append("<head>").Append(HeadTag ?? DefaultHeadTag).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(html);
build.Append("</div></div></body>");
build.Append("</html>");

View File

@@ -25,44 +25,131 @@ namespace TweetDck.Core.Other {
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.labelAbout = new TweetDck.Core.Controls.RichTextLabel();
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.labelWebsite = new System.Windows.Forms.LinkLabel();
this.tablePanelLinks = new System.Windows.Forms.TableLayoutPanel();
this.labelIssues = new System.Windows.Forms.LinkLabel();
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).BeginInit();
this.tablePanelLinks.SuspendLayout();
this.SuspendLayout();
//
// labelAbout
// pictureLogo
//
this.labelAbout.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.pictureLogo.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.pictureLogo.ErrorImage = null;
this.pictureLogo.Image = ((System.Drawing.Image)(resources.GetObject("pictureLogo.Image")));
this.pictureLogo.InitialImage = null;
this.pictureLogo.Location = new System.Drawing.Point(12, 12);
this.pictureLogo.Name = "pictureLogo";
this.pictureLogo.Size = new System.Drawing.Size(96, 96);
this.pictureLogo.TabIndex = 0;
this.pictureLogo.TabStop = false;
//
// labelDescription
//
this.labelDescription.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.labelAbout.BackColor = System.Drawing.Color.White;
this.labelAbout.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.labelAbout.Location = new System.Drawing.Point(12, 12);
this.labelAbout.Name = "labelAbout";
this.labelAbout.ReadOnly = true;
this.labelAbout.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this.labelAbout.Size = new System.Drawing.Size(360, 126);
this.labelAbout.TabIndex = 0;
this.labelAbout.TabStop = false;
this.labelAbout.Text = "";
this.labelAbout.Click += new System.EventHandler(this.labelAbout_Click);
this.labelDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelDescription.Location = new System.Drawing.Point(114, 12);
this.labelDescription.Name = "labelDescription";
this.labelDescription.Size = new System.Drawing.Size(232, 109);
this.labelDescription.TabIndex = 1;
//
// labelSourceCode
//
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);
//
// labelWebsite
//
this.labelWebsite.AutoSize = true;
this.labelWebsite.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelWebsite.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelWebsite.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
this.labelWebsite.Location = new System.Drawing.Point(0, 0);
this.labelWebsite.Margin = new System.Windows.Forms.Padding(0);
this.labelWebsite.Name = "labelWebsite";
this.labelWebsite.Size = new System.Drawing.Size(117, 16);
this.labelWebsite.TabIndex = 2;
this.labelWebsite.Text = "Official Website";
this.labelWebsite.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
this.labelWebsite.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
//
// tablePanelLinks
//
this.tablePanelLinks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tablePanelLinks.ColumnCount = 3;
this.tablePanelLinks.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 35.16F));
this.tablePanelLinks.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 29.7F));
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.Location = new System.Drawing.Point(12, 124);
this.tablePanelLinks.Name = "tablePanelLinks";
this.tablePanelLinks.RowCount = 1;
this.tablePanelLinks.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tablePanelLinks.Size = new System.Drawing.Size(334, 16);
this.tablePanelLinks.TabIndex = 4;
//
// labelIssues
//
this.labelIssues.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelIssues.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelIssues.LinkArea = new System.Windows.Forms.LinkArea(0, 0);
this.labelIssues.Location = new System.Drawing.Point(216, 0);
this.labelIssues.Margin = new System.Windows.Forms.Padding(0);
this.labelIssues.Name = "labelIssues";
this.labelIssues.Size = new System.Drawing.Size(118, 16);
this.labelIssues.TabIndex = 4;
this.labelIssues.Text = "Report an Issue";
this.labelIssues.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.labelIssues.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.OnLinkClicked);
//
// FormAbout
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.ClientSize = new System.Drawing.Size(384, 150);
this.Controls.Add(this.labelAbout);
this.ClientSize = new System.Drawing.Size(358, 152);
this.Controls.Add(this.tablePanelLinks);
this.Controls.Add(this.labelDescription);
this.Controls.Add(this.pictureLogo);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "FormAbout";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
((System.ComponentModel.ISupportInitialize)(this.pictureLogo)).EndInit();
this.tablePanelLinks.ResumeLayout(false);
this.tablePanelLinks.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private Controls.RichTextLabel labelAbout;
private System.Windows.Forms.PictureBox pictureLogo;
private System.Windows.Forms.Label labelDescription;
private System.Windows.Forms.LinkLabel labelSourceCode;
private System.Windows.Forms.LinkLabel labelWebsite;
private System.Windows.Forms.TableLayoutPanel tablePanelLinks;
private System.Windows.Forms.LinkLabel labelIssues;
}
}

View File

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

246
Core/Other/FormAbout.resx Normal file
View File

@@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureLogo.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
bGUAAEjHnZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZE
sRAUVLAHJAgoMRhFVCxvRtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs
AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnpfIHomAARm7M5GSwRF4g4
JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKjmNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR
3xKxRoowlSviN+LYVA4zAwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd
li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0NDQzMv2qUP91829K3NtF
ehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGX
wzISF/QP/U+Hv6GvvmckPu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF
hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygKESDR+8Vd/6NvvvgwIH55
4SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TPEqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJ
VgMWSASpgA+yQB7YBApBMdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB
5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeuQIPQXWgMmoZ+h97BCEyC
qbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+FO+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiE
j6xHipAKpAFpRbqRPuQmMorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I
1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+TBvmHGYQM46Zw2Kx8lh9
rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgoHA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhG
fDf+On4Cv0CQJmgT7AghhCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp
B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkjiuSReUlPSSXK1ZK5kheQJ
yeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJC
YVE2UxopFykTVAxVm+pFTaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln
yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf4aLCzFLqUtulrKVFS48v
vacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0Cvp
vfRZVUVVT1Whar3qgOqCmrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L
Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6iXo3edX1Y31Kfq79Pf9AA
bWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtrGEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0z
llmN2S1zsrm7+QbzLvMXy/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW
ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7A/ajDqoOTIcGh8eO6o5s
xybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4erkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6
eO7yHPFS8mJ5NXvNelt5r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw
YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX8LLw0QjjiHUR1yIVIrmR
XVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUKq1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNm
WS6svaxnbEd2OXuaY8cp40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl
xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BHuEU4lmmfWZP5Jiss60S2
dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNaV78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8
V5vDN3cXKBVsLBjf4rGlpVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za
Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJe4V7Ryt9K7uqNKp2Vr2v
Tqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5wD9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqb
PhziHRo9HHS4t9mqufmI4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/
0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69AzhTMGZT2dzz86dSz83cz7h
/HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9tdPnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavr
XTesb3QPLh88M+QwdP6m681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS
fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnafvvF05dOJZ+nPFmYKf5X+
tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aueuYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/
6H7o/ujz8cGn1E+f/gUDmPP8usTo0wAAAAlwSFlzAAAuIgAALiIBquLdkgAAEVpJREFUeF7tXQl0VNd5
Thq3Tk7tc+qmiZMm7YmdJj1OT3Gd0kTMG3mJnZQEJzaSjXETx0lqNwt2jWscQGixBAJLSMhgA8YYbIyQ
hHYJI4lFGMwioQWxyewgMdoXtKFd4u/337lPjDRv9pk3g+A75zsMWv577/fd+9/l3Rl9johu0Y/U/OIt
6sfrLwIYT2de+SJ4F/j34LfB+8Ap4L9J8uvvgfy9b4DfBO8EPy9DBBwC3gCI93fgDHAuuA7cDVaDzWAX
OAAOgcNgP9gN8vc+A4vB9eBr4M/Be8GAMiPgDIBAXwVDwbXgEbAPJC+RDToGvgfOBr8mi/UbAsIACMFp
ZRaYC7aBWuL5gh3gdvBZ8C5ZHV3hVwPQaM7hS0ETqCWQnqwDE8Apsnq6wC8GoJEPgFtAzt9aYviTPJ9k
gkGyuj6FrgagUd8CeVIcBLUaH0gcATeD35XV9wl0MQCN4OXjn8EroFZjA5m8qooE/1o2x6vwuQGouAEs
A7UadyPxOPgj2SyvwacGoMILwRsh3ThLTksx4BdkEz2GTwxABb8C8pJSqxGTgTvAb8jmegSvG4CK/QvI
O1Wtik8mXgSnyma7Da8agAo9Auq5kfI3+Shkhmy+W/CaAajI42CvrNjNRJ7jZksZXIZXDEAFWPxA3FTp
xVHQLRM8NgAFPwTejD1/InkH/biUxWl4ZAAK5LP3mynnO+JV0KWJ2W0DUNCXwVOy4Fu8zhrQ6SWqJwbk
WxQa2Mxop6fSWyk0rYlCUhspZEs9zdxSJ8iv+WuhW5vpqYw27d93nXvB26RUduGWAQg+36KwwCULv7VF
iDx3Wx1tPNxCBy92UU17P7VdHaLWniE619JLu8900Nv7m+jFLBgCM9gs/l3NmCqlqU+nt+D/msYtk3LZ
hcsGIPB/gPaPF7jyKrW+rwNZnJCUBoraUU9H63po9No1NM8++odGac/ZDno5r45C2QitESGF59gv5dVT
clUHzc5gEyb8XOaVa6DDsyMU67wBCHg7WCULsEkezi9/jEptbfKLCdzrn0s30b5znWiS6xgeuUaby1so
NKVubDSwGZzCOG3NzTdRQXW7+NnM4+b2atUDPAPeIeXTBEK4ZMA8i+Ca5Ir+LrOOOvtHKGkfcit6kp4m
sBh/zDGRqaMfzfEMh5CuZiXXUOgWE72Ua6L1Jc10qqmXRkbNo4nT2PMw2sHcESvl0wTCOGcAAv0DyFtv
rULGyAKsO9SMUERD6EnRO+t1M4F76++zTNTUNSjK9wYutvVhzujTTGGrMG/wqNCqiwX5IsA/SxmtgDBO
G7DBIqhN8oR3suEqQpnRh7watcOcU31pAvfCWUgZ1Y3Xy/YlCk+Z28rlqrTTvkwpoxUQyrEBCPCvoONz
fVTg1xkm6hkYQajr4Mktdpd5uecrE9jg5IpWWaJvwRN1SHKtaA/PCfMLmyiq2DxPaNUN5KMKg5RzHBDO
KQNSLYLZJFfgVSz3tMBDeM0BrMNlr9H6fXfJ8V7AErK7f1iW5hsMjYxSSnkjPbnxLP1vbq2YE04399KV
vmGak9/kqF0FUs5xQFj7BuAXvwM6ddDGOfiNnQ0IYxvbTrYjVWDiwkrFW6OB83DaEd/3/obOfiq71Ekt
3YOkzgg8NUTxPOdgLpiV1TE6K7vzASnrGBDCoQF8b0cz6ESyqDG77BvAOI2VxJycy7bX2q4QJoYgDVxo
7ZPR9QVv4OzNb7OyOll8mpnWTNPXnlwnZR0DQtg2AAHuAPnCkmbwieQREFZQjzCOwfPChlJs/3mt7cFo
YAPn5NTRsFwa6on3kII05zUIjt4uXv9icx099u5pCn7rKBkSynoMC1K/LuUVQBi7BswcF9gBWYzfZ5vE
RsZZnG/powjsVsXc4IYR/DtLix2POm+C9wGrPjWfK1nWl3v6MxA+FB1xxgeX6OF3TpKSdASsIiMMMK48
TkpM4RwprwDC2TUgTw3uFFEZXgo2dA4glPNgu8pruyms0HxAJnaWThrBP7v2oHnfoQd48xWBelqmHe7t
Is2kNNJ/rj9HD648RsoKC+FVwgBj/MGDUl4BhNQ2AIH/FuTLq1aNtkfuFYWfXUEo18FGHKvrofg9DfRs
mknEcjQq2ABejeiFyEKTWXyUbU4z7fSLj0z06NpTELnKLLyl6BOZVDVgDM+8R8ps14AQy4Y6S1fmAXvg
npZ/oo0iiuro2VSYkdIgVhocX0zc0hQ2YDWWt3phIdrGp5+hW1vpZxsv0SOcZrR6uy1iFAQvLvqDlNmu
Ae9aCus0IQzn8/LL3QjnHbAZpZe6RE9fsL2O/jvTJFIdlzMz2URRhZflT/oer+fX0E/ePUXBttKMI3Ia
enNfhpRZ2wAI+XnQ4amnLXLamPdxHQ1i4+IL8AqqHvMMp6vdZ65Q8en2sXW5r/HbjZVYzVRoi+sMYZwx
saxWWbj1Swhn04B/BPkQSVNgu+TUAHKefB/LzMkE3s3PXgfxsbLRFNd5XlMWZT6AkDYN+JmVsE5QLEPz
WujXGY0iZ3N60GOHqheuDgzT4++U80SqJarz5DQUXfAcQlobACE5/fAb4jRFtkdOPatLWqmrf4RSKlvo
1fzL9MSGM7TuQJ04R7nR0dDRSw8mlGqL6gpXniDjkp2xyoKULyCslQH81MutCZgNWLz7+qaI83Jz9yAm
0A5q6/HeGb2/cPxyBxnivWEAT8T7c4Mjc+5EWCsD/gbkt4JqimyP6rGA+sRosqHgWD0Zlh/WFtUVsgHL
S44aI3O/hrBWBvCboN273cwTMJaHvEKZjEgsOkNKogcrIJViJVTeaIzefi/CWhnA7zJvGiesC+TVT85x
8wPryYYXNpl3upqiukQYsKKyR1my63sIa2UAv3WU3xelKbAj8k71lXz/nE76Em09A/Tjt5B+PF0BqUw6
MogN2RSEtjKAHz+6twdg8k6Yz4NOuXceFKgorm7yTv5XmXRkVIk78H2EtjLgfpBv+moL7AR5Mv5NRp1X
byf4GzH5n3kn/6vkkbS8dCpCWxnAnzrikQFMPiRbWFAnbkXc6OAN2BNreAPmjfwvyQbEH9I0gD/yxfM3
W/BxRFqTuBrYOzj+lsSNht0nG72bfphJVddgwL8jvJUBHk3C4yhNeCXfRJfaPL+p5i+8ln4Cq59KbSHd
ZVLloLJs7/0Ib2UAL0NbxgnpCWEC75Bnp5rog7IW6ujz7dURb+NsYxdvmswpQ0tItyiWod1KTJHmMpQ3
YvxhR9qCukiekGenN4tHjU9+VENPbzpLcbtq6GS9954X+BLLtp/y7uTLNG/EmozRBd9GEVYG8Gf37LEU
0RNy7/+o8gqdae6jQxc76eCFTqqo7aTGzsBPSRdbeuihRG/3fpANiC85qkTkaB5F8Adr8CeaaArqKsUj
yiJ9by14C2/keXnpqXIV5pTYPblB8zZ9BcVYGcDH0f9nKaJH5I1ZSgMduNCF8DcOjtS0k+L13C/JBize
EWeYn3wbihpvgHwgw+/71RbUDfIo+FNuvdWl3UAFn+a+KM59vLzyUWl+IPMbFHVd97EXZgPuAb3yKSfq
ZSUeBbFFNSgi8PHRwRrzut8Xvd/Ma8FR+XYfSf4FyJ+PoymqM1TvRIakNtP098/TQ5h4psWXUnzBaRQT
uDhZ1yknXi/uei0JHZTlpbVBr6z7IoqzNkAlRHR9Ira4E/lEcv31O5Hq9Q2Qe9bCrJPU3T+EYgILPajT
r96v8F3qYSL/G5bszuKOjiLHeP2FBETkj5G0FlmDapp5amsrzfiwhh5ZPeFOpCXxNSWxnGauKaed2OI7
8eZF3RCejXqjbj5MPWYDInJfZo1RpF0D+EOX7L4nbOzqdWqTSDN8J3Kst2sVbkF+sMGj4fmNlZR2uFas
uYflg3t/mLJy11lf530Q6/+3qoaw+vkOa4xibRvAgMj8oaYTRLe+eu3UnUgt8mhgIxLKKBhLvsdXltDc
zRV0qUXfXfKHBy7pID4ongUfKpfyOmXAf00U3ubVa0+IOCzAM+vKqOxCG6qhHzbziodvOvhq0rUk0k9Q
WPpLUl4BVMG2AbOyu+4CW1j8Jx1dvXaHLDx6/09XHabU0loaHNb32cG7n1zQT3w+fkgs75r6Pwl3S3kF
UA3bBjB+vvH8CnH1WqYLzeCukEcNVhnc459YXSaGf0evvk/O2OjovGqZdnQQn/l2NQVF5G2Qso4B1bFv
gCEs/X6kmhHNoCp5NHBDBPm1ShYbTKwQPZ1723T09tfTT4hnrL2D+h9N17Zdpd9uNI88UUet9viCGAFB
r29WpKxjQJXsG8AwLtv3sbhOpxUYjZi+5hg9s+E4zVpfRT9ZeZh+nFRKP11ZRqFry+mFD6soKreakg/V
iPzuz/V/TqVJ1E2s83UVX9yE2294baPV3y5AtRwboIRnT0OgUavAko++fZR++cFxKj7VQgPDI9Q3OCKE
HhgKjLOfc03d9ErqcX1TjiVhADLJdCnnOKB6jg1gGOP2pwsntQpAbxLzw/JSCsuqprNocCCgoaOPEgrP
0MOJJeZjZT17vUo++Vz6yU4poxVQTScNWJT5XfSefvNmQqMgJhuBhirxJTQ/4wRVXGqnUT9c0Drf3E3L
IfyPON2Ina0fer0gVoxJVcNBr31g9QZtFaiucwYwjEt2xdqcCywpRgRWOph0f7m+QuT/y+1XfbrD5Ztr
24/V05wtR8Wmzr/CS7JWscWrpHyaQNWdN0AJS7vDmFh2zmYqmkiZmnjF8WBCibhbuWbPeTqMyZiXnp4Y
wgdnfF2cl7Gc3x9Db+ccr/sEa4u87l9R3hAcnvllKZ8m0BTnDWAo4VmPGldyIXZSkRalGdwzeWTwaonP
gRZlV9O6vRcoq8JEu7E0LTnXSidMHSKNHK29Qofwf76Xw99/Dz8XiRXV7z48QjPeLkOqg+gwN2BEtyRv
WCNyQqRsNuGyAQxlya44p1KRPaqGQDxhCnrvGFlYlRZf558TcwxP+IEmuCXFI8eda6RcduGWAcaFaX9p
jD/0qdOp6GYia7K8pCp4UfqXpFx24ZYBDGVB6jeNKypMt0ywoPm8p9UYkW3zI8omwm0DGIbXN//AuKKy
VxSsVaGbiWYNhrBpdenPnHhkAAMTzUwUPnxTm6C2PWrbc1IWp+GxAQxDePavxlXkpiLazO2OKfqjlMMl
eMUABruPCt1kI4HbiuVm9PY/SRlchtcMYBjCMkJQMcwJN8HEzB0tqWrIGF3wvGy+W/CqAQwlLH2aknDY
xGthzYpPBnIHW1HRYozMeUw222143QDGtD8n3yP2CcKESZaSeAOaUFpuDM+8TzbXI/jEAIZxYepfGaIL
48yVngQpiVMO2qHEFq81Lkr32p819JkBKgyLMmeYD/Bu4NHAdU8sr1Ui85+SzfIafG4Aw7go4y5jbPEy
8TzhRpobxMitGjYu3fOOMWzrV2VzvApdDFDxw3mbpyhL92aL09RATkuibhitb35agIn2h7L6PoGuBqgw
hOc8Zow7UCQayyMiIPYOnONlmow7sEeJzPPoL+Q5C78YoAI76KBpMTtXK4nlLcZV6HVqz9MUyEfkMpkr
KnuMSz/ZpETkPCyrpwv8aoCKH8zdcPe0yG0vYum6X8wTLIivRoZczQgmVQ2izBIluvBVZeHWb8nq6IqA
MMASWDX9k+GN7X8wxO5JU5aXnjeLxoKxIVI4YYzKCQKrX7cUWpiJf/n7CWW1yrJ9WcaYwpeU8Cynj419
hYAzwBJB8zbdHhyVd78xpuA5ZfHOWEzgWeixFVgSNiBldGG0DICj4uE7PyEzvx4Q6YQ/ECm+5Cgm0jxl
8Y5lyhsfPw9zpyrzU3zyp8ndRUAbwFDrZViQdlvQ/NQ7kKPvNkYX3GuIKbpPWfrJFP7IFyXu4FQYM1V5
c//3lWV7p/A70JWobfcq4dlfN4Zn3WmYn3y7GseSgYCxulhW7Bb1p+YXb1Ev0uf+H9A3E1Z4VJUaAAAA
AElFTkSuQmCC
</value>
</data>
</root>

108
Core/Other/FormPlugins.Designer.cs generated Normal file
View File

@@ -0,0 +1,108 @@
namespace TweetDck.Core.Other {
partial class FormPlugins {
/// <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() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormPlugins));
this.btnClose = new System.Windows.Forms.Button();
this.btnReload = new System.Windows.Forms.Button();
this.btnOpenFolder = new System.Windows.Forms.Button();
this.tabPanelPlugins = new TweetDck.Core.Controls.TabPanel();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.AutoSize = true;
this.btnClose.Location = new System.Drawing.Point(643, 439);
this.btnClose.Name = "btnClose";
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnClose.Size = new System.Drawing.Size(49, 23);
this.btnClose.TabIndex = 1;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// btnReload
//
this.btnReload.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnReload.AutoSize = true;
this.btnReload.Location = new System.Drawing.Point(131, 439);
this.btnReload.Name = "btnReload";
this.btnReload.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnReload.Size = new System.Drawing.Size(71, 23);
this.btnReload.TabIndex = 2;
this.btnReload.Text = "Reload All";
this.btnReload.UseVisualStyleBackColor = true;
this.btnReload.Click += new System.EventHandler(this.btnReload_Click);
//
// btnOpenFolder
//
this.btnOpenFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnOpenFolder.AutoSize = true;
this.btnOpenFolder.Location = new System.Drawing.Point(12, 439);
this.btnOpenFolder.Name = "btnOpenFolder";
this.btnOpenFolder.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnOpenFolder.Size = new System.Drawing.Size(113, 23);
this.btnOpenFolder.TabIndex = 3;
this.btnOpenFolder.Text = "Open Plugin Folder";
this.btnOpenFolder.UseVisualStyleBackColor = true;
this.btnOpenFolder.Click += new System.EventHandler(this.btnOpenFolder_Click);
//
// pluginList
//
this.tabPanelPlugins.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.tabPanelPlugins.Location = new System.Drawing.Point(12, 12);
this.tabPanelPlugins.Name = "tabPanelPlugins";
this.tabPanelPlugins.Size = new System.Drawing.Size(680, 421);
this.tabPanelPlugins.TabIndex = 4;
//
// FormPlugins
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(704, 474);
this.Controls.Add(this.tabPanelPlugins);
this.Controls.Add(this.btnOpenFolder);
this.Controls.Add(this.btnReload);
this.Controls.Add(this.btnClose);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MinimumSize = new System.Drawing.Size(480, 320);
this.Name = "FormPlugins";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Plugins";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnReload;
private System.Windows.Forms.Button btnOpenFolder;
private TweetDck.Core.Controls.TabPanel tabPanelPlugins;
}
}

95
Core/Other/FormPlugins.cs Normal file
View File

@@ -0,0 +1,95 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Plugins;
using TweetDck.Plugins.Controls;
using TweetDck.Plugins.Events;
namespace TweetDck.Core.Other{
partial class FormPlugins : Form{
private readonly PluginManager pluginManager;
private readonly TabButton tabBtnOfficial, tabBtnCustom;
private readonly PluginListFlowLayout flowLayoutPlugins;
private PluginGroup? selectedGroup;
public FormPlugins(){
InitializeComponent();
}
public FormPlugins(PluginManager pluginManager) : this(){
this.pluginManager = pluginManager;
this.pluginManager.Reloaded += pluginManager_Reloaded;
this.flowLayoutPlugins = new PluginListFlowLayout();
this.flowLayoutPlugins.Resize += flowLayoutPlugins_Resize;
this.tabPanelPlugins.SetupTabPanel(90);
this.tabPanelPlugins.ReplaceContent(flowLayoutPlugins);
this.tabBtnOfficial = tabPanelPlugins.AddButton("",() => SelectGroup(PluginGroup.Official));
this.tabBtnCustom = tabPanelPlugins.AddButton("",() => SelectGroup(PluginGroup.Custom));
this.tabPanelPlugins.SelectTab(tabBtnOfficial);
this.pluginManager_Reloaded(pluginManager,null);
Shown += (sender, args) => {
Program.UserConfig.PluginsWindow.Restore(this,false);
};
FormClosed += (sender, args) => {
Program.UserConfig.PluginsWindow.Save(this);
Program.UserConfig.Save();
};
}
private void SelectGroup(PluginGroup group){
if (selectedGroup.HasValue && selectedGroup == group)return;
selectedGroup = group;
ReloadPluginTab();
}
public void ReloadPluginTab(){
if (!selectedGroup.HasValue)return;
flowLayoutPlugins.SuspendLayout();
flowLayoutPlugins.Controls.Clear();
foreach(Plugin plugin in pluginManager.GetPluginsByGroup(selectedGroup.Value)){
flowLayoutPlugins.Controls.Add(new PluginControl(pluginManager,plugin));
}
flowLayoutPlugins_Resize(flowLayoutPlugins,new EventArgs());
flowLayoutPlugins.ResumeLayout(true);
}
private void pluginManager_Reloaded(object sender, PluginLoadEventArgs e){
tabBtnOfficial.Text = "Official: "+pluginManager.CountPluginByGroup(PluginGroup.Official);
tabBtnCustom.Text = "Custom: "+pluginManager.CountPluginByGroup(PluginGroup.Custom);
}
private void flowLayoutPlugins_Resize(object sender, EventArgs e){
int horizontalOffset = 8+(flowLayoutPlugins.VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0);
foreach(Control control in flowLayoutPlugins.Controls){
control.Width = flowLayoutPlugins.Width-control.Margin.Horizontal-horizontalOffset;
}
}
private void btnOpenFolder_Click(object sender, EventArgs e){
using(Process.Start("explorer.exe","\""+pluginManager.PathCustomPlugins+"\"")){}
}
private void btnReload_Click(object sender, EventArgs e){
pluginManager.Reload();
ReloadPluginTab();
}
private void btnClose_Click(object sender, EventArgs e){
Close();
}
}
}

View File

@@ -24,348 +24,85 @@
/// </summary>
private void InitializeComponent() {
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormSettings));
this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
this.labelDisplay = new System.Windows.Forms.Label();
this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
this.labelEdgeDistance = new System.Windows.Forms.Label();
this.trackBarEdgeDistance = new System.Windows.Forms.TrackBar();
this.radioLocCustom = new System.Windows.Forms.RadioButton();
this.radioLocBR = new System.Windows.Forms.RadioButton();
this.radioLocBL = new System.Windows.Forms.RadioButton();
this.radioLocTR = new System.Windows.Forms.RadioButton();
this.radioLocTL = new System.Windows.Forms.RadioButton();
this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
this.tableColumn2Panel = new System.Windows.Forms.Panel();
this.groupUserInterface = new System.Windows.Forms.GroupBox();
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayType = new System.Windows.Forms.Label();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
this.radioDurVeryLong = new System.Windows.Forms.RadioButton();
this.radioDurLong = new System.Windows.Forms.RadioButton();
this.radioDurMedium = new System.Windows.Forms.RadioButton();
this.radioDurShort = new System.Windows.Forms.RadioButton();
this.groupNotificationLocation.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.tableLayout.SuspendLayout();
this.tableColumn2Panel.SuspendLayout();
this.groupUserInterface.SuspendLayout();
this.groupNotificationDuration.SuspendLayout();
this.tabPanel = new TweetDck.Core.Controls.TabPanel();
this.btnClose = new System.Windows.Forms.Button();
this.btnExport = new System.Windows.Forms.Button();
this.btnImport = new System.Windows.Forms.Button();
this.btnReset = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// groupNotificationLocation
// tabPanel
//
this.groupNotificationLocation.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
this.tabPanel.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.groupNotificationLocation.Controls.Add(this.labelDisplay);
this.groupNotificationLocation.Controls.Add(this.comboBoxDisplay);
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistance);
this.groupNotificationLocation.Controls.Add(this.trackBarEdgeDistance);
this.groupNotificationLocation.Controls.Add(this.radioLocCustom);
this.groupNotificationLocation.Controls.Add(this.radioLocBR);
this.groupNotificationLocation.Controls.Add(this.radioLocBL);
this.groupNotificationLocation.Controls.Add(this.radioLocTR);
this.groupNotificationLocation.Controls.Add(this.radioLocTL);
this.groupNotificationLocation.Location = new System.Drawing.Point(6, 6);
this.groupNotificationLocation.Name = "groupNotificationLocation";
this.groupNotificationLocation.Size = new System.Drawing.Size(183, 278);
this.groupNotificationLocation.TabIndex = 0;
this.groupNotificationLocation.TabStop = false;
this.groupNotificationLocation.Text = "Notification Location";
this.tabPanel.Location = new System.Drawing.Point(12, 12);
this.tabPanel.Name = "tabPanel";
this.tabPanel.Size = new System.Drawing.Size(480, 313);
this.tabPanel.TabIndex = 3;
//
// labelDisplay
// btnClose
//
this.labelDisplay.AutoSize = true;
this.labelDisplay.Location = new System.Drawing.Point(6, 148);
this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDisplay.Name = "labelDisplay";
this.labelDisplay.Size = new System.Drawing.Size(41, 13);
this.labelDisplay.TabIndex = 8;
this.labelDisplay.Text = "Display";
this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnClose.AutoSize = true;
this.btnClose.Location = new System.Drawing.Point(443, 331);
this.btnClose.Name = "btnClose";
this.btnClose.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnClose.Size = new System.Drawing.Size(49, 23);
this.btnClose.TabIndex = 4;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
//
// comboBoxDisplay
// btnExport
//
this.comboBoxDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDisplay.FormattingEnabled = true;
this.comboBoxDisplay.Location = new System.Drawing.Point(9, 164);
this.comboBoxDisplay.Name = "comboBoxDisplay";
this.comboBoxDisplay.Size = new System.Drawing.Size(168, 21);
this.comboBoxDisplay.TabIndex = 7;
this.comboBoxDisplay.SelectedValueChanged += new System.EventHandler(this.comboBoxDisplay_SelectedValueChanged);
this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnExport.AutoSize = true;
this.btnExport.Location = new System.Drawing.Point(12, 331);
this.btnExport.Name = "btnExport";
this.btnExport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnExport.Size = new System.Drawing.Size(94, 23);
this.btnExport.TabIndex = 5;
this.btnExport.Text = "Export Settings";
this.btnExport.UseVisualStyleBackColor = true;
this.btnExport.Click += new System.EventHandler(this.btnExport_Click);
//
// labelEdgeDistance
// btnImport
//
this.labelEdgeDistance.AutoSize = true;
this.labelEdgeDistance.Location = new System.Drawing.Point(6, 197);
this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelEdgeDistance.Name = "labelEdgeDistance";
this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13);
this.labelEdgeDistance.TabIndex = 6;
this.labelEdgeDistance.Text = "Distance From Edge";
this.btnImport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnImport.AutoSize = true;
this.btnImport.Location = new System.Drawing.Point(112, 331);
this.btnImport.Name = "btnImport";
this.btnImport.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnImport.Size = new System.Drawing.Size(94, 23);
this.btnImport.TabIndex = 6;
this.btnImport.Text = "Import Settings";
this.btnImport.UseVisualStyleBackColor = true;
this.btnImport.Click += new System.EventHandler(this.btnImport_Click);
//
// trackBarEdgeDistance
// btnReset
//
this.trackBarEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarEdgeDistance.LargeChange = 8;
this.trackBarEdgeDistance.Location = new System.Drawing.Point(6, 213);
this.trackBarEdgeDistance.Maximum = 40;
this.trackBarEdgeDistance.Minimum = 8;
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
this.trackBarEdgeDistance.Size = new System.Drawing.Size(171, 45);
this.trackBarEdgeDistance.SmallChange = 2;
this.trackBarEdgeDistance.TabIndex = 5;
this.trackBarEdgeDistance.TickFrequency = 2;
this.trackBarEdgeDistance.Value = 8;
this.trackBarEdgeDistance.ValueChanged += new System.EventHandler(this.trackBarEdgeDistance_ValueChanged);
//
// radioLocCustom
//
this.radioLocCustom.AutoSize = true;
this.radioLocCustom.Location = new System.Drawing.Point(7, 116);
this.radioLocCustom.Name = "radioLocCustom";
this.radioLocCustom.Size = new System.Drawing.Size(60, 17);
this.radioLocCustom.TabIndex = 4;
this.radioLocCustom.TabStop = true;
this.radioLocCustom.Text = "Custom";
this.radioLocCustom.UseVisualStyleBackColor = true;
this.radioLocCustom.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocCustom.Click += new System.EventHandler(this.radioLoc_Click);
//
// radioLocBR
//
this.radioLocBR.AutoSize = true;
this.radioLocBR.Location = new System.Drawing.Point(7, 92);
this.radioLocBR.Name = "radioLocBR";
this.radioLocBR.Size = new System.Drawing.Size(86, 17);
this.radioLocBR.TabIndex = 3;
this.radioLocBR.TabStop = true;
this.radioLocBR.Text = "Bottom Right";
this.radioLocBR.UseVisualStyleBackColor = true;
this.radioLocBR.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocBR.Click += new System.EventHandler(this.radioLoc_Click);
//
// radioLocBL
//
this.radioLocBL.AutoSize = true;
this.radioLocBL.Location = new System.Drawing.Point(7, 68);
this.radioLocBL.Name = "radioLocBL";
this.radioLocBL.Size = new System.Drawing.Size(79, 17);
this.radioLocBL.TabIndex = 2;
this.radioLocBL.TabStop = true;
this.radioLocBL.Text = "Bottom Left";
this.radioLocBL.UseVisualStyleBackColor = true;
this.radioLocBL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocBL.Click += new System.EventHandler(this.radioLoc_Click);
//
// radioLocTR
//
this.radioLocTR.AutoSize = true;
this.radioLocTR.Location = new System.Drawing.Point(7, 44);
this.radioLocTR.Name = "radioLocTR";
this.radioLocTR.Size = new System.Drawing.Size(72, 17);
this.radioLocTR.TabIndex = 1;
this.radioLocTR.TabStop = true;
this.radioLocTR.Text = "Top Right";
this.radioLocTR.UseVisualStyleBackColor = true;
this.radioLocTR.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocTR.Click += new System.EventHandler(this.radioLoc_Click);
//
// radioLocTL
//
this.radioLocTL.AutoSize = true;
this.radioLocTL.Location = new System.Drawing.Point(7, 20);
this.radioLocTL.Name = "radioLocTL";
this.radioLocTL.Size = new System.Drawing.Size(65, 17);
this.radioLocTL.TabIndex = 0;
this.radioLocTL.TabStop = true;
this.radioLocTL.Text = "Top Left";
this.radioLocTL.UseVisualStyleBackColor = true;
this.radioLocTL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
this.radioLocTL.Click += new System.EventHandler(this.radioLoc_Click);
//
// tableLayout
//
this.tableLayout.ColumnCount = 2;
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayout.Controls.Add(this.tableColumn2Panel, 1, 0);
this.tableLayout.Controls.Add(this.groupNotificationLocation, 0, 0);
this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayout.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize;
this.tableLayout.Location = new System.Drawing.Point(0, 0);
this.tableLayout.Name = "tableLayout";
this.tableLayout.Padding = new System.Windows.Forms.Padding(3);
this.tableLayout.RowCount = 1;
this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayout.Size = new System.Drawing.Size(384, 290);
this.tableLayout.TabIndex = 2;
//
// tableColumn2Panel
//
this.tableColumn2Panel.Controls.Add(this.groupUserInterface);
this.tableColumn2Panel.Controls.Add(this.groupNotificationDuration);
this.tableColumn2Panel.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableColumn2Panel.Location = new System.Drawing.Point(192, 3);
this.tableColumn2Panel.Margin = new System.Windows.Forms.Padding(0);
this.tableColumn2Panel.Name = "tableColumn2Panel";
this.tableColumn2Panel.Size = new System.Drawing.Size(189, 284);
this.tableColumn2Panel.TabIndex = 3;
//
// groupUserInterface
//
this.groupUserInterface.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.groupUserInterface.Controls.Add(this.checkExpandLinks);
this.groupUserInterface.Controls.Add(this.comboBoxTrayType);
this.groupUserInterface.Controls.Add(this.labelTrayType);
this.groupUserInterface.Controls.Add(this.checkUpdateNotifications);
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Location = new System.Drawing.Point(3, 128);
this.groupUserInterface.Name = "groupUserInterface";
this.groupUserInterface.Size = new System.Drawing.Size(183, 153);
this.groupUserInterface.TabIndex = 3;
this.groupUserInterface.TabStop = false;
this.groupUserInterface.Text = "User Interface";
//
// checkExpandLinks
//
this.checkExpandLinks.AutoSize = true;
this.checkExpandLinks.Location = new System.Drawing.Point(6, 45);
this.checkExpandLinks.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3);
this.checkExpandLinks.Name = "checkExpandLinks";
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
this.checkExpandLinks.TabIndex = 11;
this.checkExpandLinks.Text = "Expand Links When Hovered";
this.checkExpandLinks.UseVisualStyleBackColor = true;
this.checkExpandLinks.CheckedChanged += new System.EventHandler(this.checkExpandLinks_CheckedChanged);
//
// comboBoxTrayType
//
this.comboBoxTrayType.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(9, 116);
this.comboBoxTrayType.Margin = new System.Windows.Forms.Padding(3, 3, 3, 12);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(168, 21);
this.comboBoxTrayType.TabIndex = 10;
this.comboBoxTrayType.SelectedIndexChanged += new System.EventHandler(this.comboBoxTrayType_SelectedIndexChanged);
//
// labelTrayType
//
this.labelTrayType.AutoSize = true;
this.labelTrayType.Location = new System.Drawing.Point(6, 100);
this.labelTrayType.Margin = new System.Windows.Forms.Padding(3, 11, 3, 0);
this.labelTrayType.Name = "labelTrayType";
this.labelTrayType.Size = new System.Drawing.Size(52, 13);
this.labelTrayType.TabIndex = 9;
this.labelTrayType.Text = "Tray Icon";
//
// checkUpdateNotifications
//
this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(6, 69);
this.checkUpdateNotifications.Margin = new System.Windows.Forms.Padding(3, 4, 3, 3);
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(115, 17);
this.checkUpdateNotifications.TabIndex = 5;
this.checkUpdateNotifications.Text = "Check for Updates";
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
this.checkUpdateNotifications.CheckedChanged += new System.EventHandler(this.checkUpdateNotifications_CheckedChanged);
//
// checkNotificationTimer
//
this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(6, 21);
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
this.checkNotificationTimer.Name = "checkNotificationTimer";
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
this.checkNotificationTimer.TabIndex = 4;
this.checkNotificationTimer.Text = "Display Notification Timer";
this.checkNotificationTimer.UseVisualStyleBackColor = true;
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
//
// groupNotificationDuration
//
this.groupNotificationDuration.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.groupNotificationDuration.Controls.Add(this.radioDurVeryLong);
this.groupNotificationDuration.Controls.Add(this.radioDurLong);
this.groupNotificationDuration.Controls.Add(this.radioDurMedium);
this.groupNotificationDuration.Controls.Add(this.radioDurShort);
this.groupNotificationDuration.Location = new System.Drawing.Point(3, 3);
this.groupNotificationDuration.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 119);
this.groupNotificationDuration.TabIndex = 2;
this.groupNotificationDuration.TabStop = false;
this.groupNotificationDuration.Text = "Notification Duration";
//
// radioDurVeryLong
//
this.radioDurVeryLong.AutoSize = true;
this.radioDurVeryLong.Location = new System.Drawing.Point(6, 92);
this.radioDurVeryLong.Name = "radioDurVeryLong";
this.radioDurVeryLong.Size = new System.Drawing.Size(73, 17);
this.radioDurVeryLong.TabIndex = 3;
this.radioDurVeryLong.TabStop = true;
this.radioDurVeryLong.Text = "Very Long";
this.radioDurVeryLong.UseVisualStyleBackColor = true;
this.radioDurVeryLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurVeryLong.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurLong
//
this.radioDurLong.AutoSize = true;
this.radioDurLong.Location = new System.Drawing.Point(6, 68);
this.radioDurLong.Name = "radioDurLong";
this.radioDurLong.Size = new System.Drawing.Size(49, 17);
this.radioDurLong.TabIndex = 2;
this.radioDurLong.TabStop = true;
this.radioDurLong.Text = "Long";
this.radioDurLong.UseVisualStyleBackColor = true;
this.radioDurLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurLong.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurMedium
//
this.radioDurMedium.AutoSize = true;
this.radioDurMedium.Location = new System.Drawing.Point(6, 44);
this.radioDurMedium.Name = "radioDurMedium";
this.radioDurMedium.Size = new System.Drawing.Size(62, 17);
this.radioDurMedium.TabIndex = 1;
this.radioDurMedium.TabStop = true;
this.radioDurMedium.Text = "Medium";
this.radioDurMedium.UseVisualStyleBackColor = true;
this.radioDurMedium.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurMedium.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurShort
//
this.radioDurShort.AutoSize = true;
this.radioDurShort.Location = new System.Drawing.Point(6, 20);
this.radioDurShort.Name = "radioDurShort";
this.radioDurShort.Size = new System.Drawing.Size(50, 17);
this.radioDurShort.TabIndex = 0;
this.radioDurShort.TabStop = true;
this.radioDurShort.Text = "Short";
this.radioDurShort.UseVisualStyleBackColor = true;
this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click);
this.btnReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnReset.AutoSize = true;
this.btnReset.Location = new System.Drawing.Point(212, 331);
this.btnReset.Name = "btnReset";
this.btnReset.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnReset.Size = new System.Drawing.Size(102, 23);
this.btnReset.TabIndex = 7;
this.btnReset.Text = "Restore Defaults";
this.btnReset.UseVisualStyleBackColor = true;
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
//
// FormSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(384, 290);
this.Controls.Add(this.tableLayout);
this.ClientSize = new System.Drawing.Size(504, 366);
this.Controls.Add(this.btnReset);
this.Controls.Add(this.btnImport);
this.Controls.Add(this.btnExport);
this.Controls.Add(this.btnClose);
this.Controls.Add(this.tabPanel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
@@ -373,43 +110,17 @@
this.Name = "FormSettings";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FormSettings_FormClosing);
this.groupNotificationLocation.ResumeLayout(false);
this.groupNotificationLocation.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.tableLayout.ResumeLayout(false);
this.tableColumn2Panel.ResumeLayout(false);
this.groupUserInterface.ResumeLayout(false);
this.groupUserInterface.PerformLayout();
this.groupNotificationDuration.ResumeLayout(false);
this.groupNotificationDuration.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.GroupBox groupNotificationLocation;
private System.Windows.Forms.RadioButton radioLocCustom;
private System.Windows.Forms.RadioButton radioLocBR;
private System.Windows.Forms.RadioButton radioLocBL;
private System.Windows.Forms.RadioButton radioLocTR;
private System.Windows.Forms.RadioButton radioLocTL;
private System.Windows.Forms.Label labelEdgeDistance;
private System.Windows.Forms.TrackBar trackBarEdgeDistance;
private System.Windows.Forms.Label labelDisplay;
private System.Windows.Forms.ComboBox comboBoxDisplay;
private System.Windows.Forms.TableLayoutPanel tableLayout;
private System.Windows.Forms.Panel tableColumn2Panel;
private System.Windows.Forms.GroupBox groupUserInterface;
private System.Windows.Forms.GroupBox groupNotificationDuration;
private System.Windows.Forms.RadioButton radioDurVeryLong;
private System.Windows.Forms.RadioButton radioDurLong;
private System.Windows.Forms.RadioButton radioDurMedium;
private System.Windows.Forms.RadioButton radioDurShort;
private System.Windows.Forms.CheckBox checkNotificationTimer;
private System.Windows.Forms.CheckBox checkUpdateNotifications;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayType;
private System.Windows.Forms.CheckBox checkExpandLinks;
private Controls.TabPanel tabPanel;
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.Button btnExport;
private System.Windows.Forms.Button btnImport;
private System.Windows.Forms.Button btnReset;
}
}

View File

@@ -1,153 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using TweetDck.Configuration;
using TweetDck.Core.Handling;
using TweetDck.Core.Other.Settings;
using TweetDck.Core.Other.Settings.Export;
using TweetDck.Updates;
namespace TweetDck.Core.Other{
sealed partial class FormSettings : Form{
private static UserConfig Config{
get{
return Program.UserConfig;
}
}
private readonly Dictionary<Type,BaseTabSettings> tabs = new Dictionary<Type,BaseTabSettings>(4);
private readonly FormNotification notification;
private bool isLoaded;
public FormSettings(FormBrowser browserForm){
public FormSettings(FormBrowser browserForm, UpdateHandler updates){
InitializeComponent();
Shown += (sender, args) => isLoaded = true;
Text = Program.BrandName+" Settings";
notification = new FormNotification(browserForm,false){ CanMoveWindow = () => radioLocCustom.Checked };
notification.Move += (sender, args) => {
if (radioLocCustom.Checked){
Config.CustomNotificationPosition = notification.Location;
}
};
notification.Show(this);
switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
case TweetNotification.Position.BottomLeft: radioLocBL.Checked = true; break;
case TweetNotification.Position.BottomRight: radioLocBR.Checked = true; break;
case TweetNotification.Position.Custom: radioLocCustom.Checked = true; break;
this.tabPanel.SetupTabPanel(100);
this.tabPanel.AddButton("General",SelectTab<TabSettingsGeneral>);
this.tabPanel.AddButton("Notifications",() => SelectTab(() => new TabSettingsNotifications(browserForm.CreateNotificationForm(false))));
this.tabPanel.AddButton("Updates",() => SelectTab(() => new TabSettingsUpdates(updates)));
this.tabPanel.AddButton("Advanced",SelectTab<TabSettingsAdvanced>);
this.tabPanel.SelectTab(tabPanel.Buttons.First());
}
switch(Config.NotificationDuration){
case TweetNotification.Duration.Short: radioDurShort.Checked = true; break;
case TweetNotification.Duration.Medium: radioDurMedium.Checked = true; break;
case TweetNotification.Duration.Long: radioDurLong.Checked = true; break;
case TweetNotification.Duration.VeryLong: radioDurVeryLong.Checked = true; break;
private void SelectTab<T>() where T : BaseTabSettings, new(){
SelectTab(() => new T());
}
comboBoxDisplay.Items.Add("(Same As "+Program.BrandName+")");
private void SelectTab<T>(Func<T> constructor) where T : BaseTabSettings{
BaseTabSettings control;
foreach(Screen screen in Screen.AllScreens){
comboBoxDisplay.Items.Add(screen.DeviceName+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
if (tabs.TryGetValue(typeof(T),out control)){
tabPanel.ReplaceContent(control);
}
else{
control = tabs[typeof(T)] = constructor();
control.Ready = true;
tabPanel.ReplaceContent(control);
}
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay);
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior,0),comboBoxTrayType.Items.Count-1);
trackBarEdgeDistance.Value = Config.NotificationEdgeDistance;
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
checkExpandLinks.Checked = Config.ExpandLinksOnHover;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
}
private void FormSettings_FormClosing(object sender, FormClosingEventArgs e){
Config.Save();
Program.UserConfig.Save();
}
private void radioLoc_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
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);
if (resultSaveCredentials == DialogResult.Cancel)return;
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft;
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight;
else if (radioLocBL.Checked)Config.NotificationPosition = TweetNotification.Position.BottomLeft;
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
else if (radioLocCustom.Checked){
if (!Config.IsCustomNotificationPositionSet){
Config.CustomNotificationPosition = notification.Location;
bool saveCredentials = resultSaveCredentials == DialogResult.Yes;
string file;
using(SaveFileDialog dialog = new SaveFileDialog{
AddExtension = true,
AutoUpgradeEnabled = true,
OverwritePrompt = true,
DefaultExt = "tdsettings",
FileName = Program.BrandName+".tdsettings",
Title = "Export "+Program.BrandName+" Settings",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
}){
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
Config.NotificationPosition = TweetNotification.Position.Custom;
}
if (file != null){
Program.UserConfig.Save();
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
notification.ShowNotificationForSettings(false);
}
ExportManager manager = new ExportManager(file);
private void radioLoc_Click(object sender, EventArgs e){
if (!isLoaded)return;
notification.ShowNotificationForSettings(false);
}
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
notification.ShowNotificationForSettings(false);
}
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
notification.ShowNotificationForSettings(false);
}
private void radioDur_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
if (radioDurShort.Checked)Config.NotificationDuration = TweetNotification.Duration.Short;
else if (radioDurMedium.Checked)Config.NotificationDuration = TweetNotification.Duration.Medium;
else if (radioDurLong.Checked)Config.NotificationDuration = TweetNotification.Duration.Long;
else if (radioDurVeryLong.Checked)Config.NotificationDuration = TweetNotification.Duration.VeryLong;
notification.ShowNotificationForSettings(true);
}
private void radioDur_Click(object sender, EventArgs e){
if (!isLoaded)return;
notification.ShowNotificationForSettings(true);
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
}
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
notification.ShowNotificationForSettings(true);
}
private void checkExpandLinks_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
}
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
if (!isLoaded)return;
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
if (!manager.Export(saveCredentials)){
Program.HandleException("An exception happened while exporting "+Program.BrandName+" settings.",manager.LastException);
}
}
}
private void btnImport_Click(object sender, EventArgs e){
string file;
using(OpenFileDialog dialog = new OpenFileDialog{
AutoUpgradeEnabled = true,
DereferenceLinks = true,
Title = "Import "+Program.BrandName+" Settings",
Filter = Program.BrandName+" Settings (*.tdsettings)|*.tdsettings"
}){
file = dialog.ShowDialog() == DialogResult.OK ? dialog.FileName : null;
}
if (file != null){
ExportManager manager = new ExportManager(file);
if (manager.Import()){
ReloadUI();
}
else{
Program.HandleException("An exception happened while importing "+Program.BrandName+" settings.",manager.LastException);
}
}
}
private void btnReset_Click(object sender, EventArgs e){
if (MessageBox.Show("This will reset all of your settings, including disabled plugins. Do you want to proceed?","Reset "+Program.BrandName+" Settings",MessageBoxButtons.YesNo,MessageBoxIcon.Warning,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
Program.ResetConfig();
ReloadUI();
}
}
private void btnClose_Click(object sender, EventArgs e){
Close();
}
private void ReloadUI(){
tabs.Clear();
tabPanel.Content.Controls.Clear();
tabPanel.ActiveButton.Callback();
}
}
}

View File

@@ -0,0 +1,3 @@
namespace TweetDck.Core.Other.Settings{
partial class BaseTabSettings{}
}

View File

@@ -0,0 +1,18 @@
using System.Windows.Forms;
using TweetDck.Configuration;
namespace TweetDck.Core.Other.Settings{
partial class BaseTabSettings : UserControl{
protected static UserConfig Config{
get{
return Program.UserConfig;
}
}
public bool Ready { get; set; }
public BaseTabSettings(){
Padding = new Padding(6,6,6,6);
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.IO;
using System.Text;
namespace TweetDck.Core.Other.Settings.Export{
class CombinedFileStream : IDisposable{
private readonly Stream stream;
public CombinedFileStream(Stream stream){
this.stream = stream;
}
public void WriteFile(string identifier, string path){
byte[] contents;
using(FileStream fileStream = new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)){
int index = 0;
int left = (int)fileStream.Length;
contents = new byte[left];
while(left > 0){
int read = fileStream.Read(contents,index,left);
index += read;
left -= read;
}
}
byte[] name = Encoding.UTF8.GetBytes(identifier);
byte[] contentsLength = BitConverter.GetBytes(contents.Length);
stream.WriteByte((byte)name.Length);
stream.Write(name,0,name.Length);
stream.Write(contentsLength,0,4);
stream.Write(contents,0,contents.Length);
}
public Entry ReadFile(){
int nameLength = stream.ReadByte();
if (nameLength == -1){
return null;
}
byte[] name = new byte[nameLength];
stream.Read(name,0,nameLength);
byte[] contentLength = new byte[4];
stream.Read(contentLength,0,4);
byte[] contents = new byte[BitConverter.ToInt32(contentLength,0)];
stream.Read(contents,0,contents.Length);
return new Entry(Encoding.UTF8.GetString(name),contents);
}
public void Flush(){
stream.Flush();
}
void IDisposable.Dispose(){
stream.Dispose();
}
public class Entry{
public string Identifier { get; private set; }
private readonly byte[] contents;
public Entry(string identifier, byte[] contents){
this.Identifier = identifier;
this.contents = contents;
}
public void WriteToFile(string path){
File.WriteAllBytes(path,contents);
}
}
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace TweetDck.Core.Other.Settings.Export{
sealed class ExportManager{
public static readonly string CookiesPath = Path.Combine(Program.StoragePath,"Cookies");
public static readonly string TempCookiesPath = Path.Combine(Program.StoragePath,"CookiesTmp");
public Exception LastException { get; private set; }
private readonly string file;
public ExportManager(string file){
this.file = file;
}
public bool Export(bool includeSession){
try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file,FileMode.Create,FileAccess.Write,FileShare.None))){
stream.WriteFile("config",Program.ConfigFilePath);
if (includeSession){
stream.WriteFile("cookies",CookiesPath);
}
stream.Flush();
}
return true;
}catch(Exception e){
LastException = e;
return false;
}
}
public bool Import(){
try{
using(CombinedFileStream stream = new CombinedFileStream(new FileStream(file,FileMode.Open,FileAccess.Read,FileShare.None))){
CombinedFileStream.Entry entry;
while((entry = stream.ReadFile()) != null){
switch(entry.Identifier){
case "config":
entry.WriteToFile(Program.ConfigFilePath);
Program.ReloadConfig();
break;
case "cookies":
if (MessageBox.Show("Do you want to import the login session? This will restart "+Program.BrandName+".","Importing "+Program.BrandName+" Settings",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
entry.WriteToFile(Path.Combine(Program.StoragePath,TempCookiesPath));
// okay to and restart, 'cookies' is always the last entry
Process.Start(Application.ExecutablePath,"-restart -importcookies");
Application.Exit();
}
break;
}
}
}
return true;
}catch(Exception e){
LastException = e;
return false;
}
}
}
}

View File

@@ -0,0 +1,82 @@
namespace TweetDck.Core.Other.Settings {
partial class TabSettingsAdvanced {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.btnClearCache = new System.Windows.Forms.Button();
this.labelMiscellaneous = new System.Windows.Forms.Label();
this.checkHardwareAcceleration = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// btnClearCache
//
this.btnClearCache.Location = new System.Drawing.Point(12, 56);
this.btnClearCache.Name = "btnClearCache";
this.btnClearCache.Size = new System.Drawing.Size(171, 23);
this.btnClearCache.TabIndex = 14;
this.btnClearCache.Text = "Clear Cache (calculating)";
this.btnClearCache.UseVisualStyleBackColor = true;
this.btnClearCache.Click += new System.EventHandler(this.btnClearCache_Click);
//
// labelMiscellaneous
//
this.labelMiscellaneous.AutoSize = true;
this.labelMiscellaneous.Location = new System.Drawing.Point(9, 40);
this.labelMiscellaneous.Margin = new System.Windows.Forms.Padding(3, 11, 3, 0);
this.labelMiscellaneous.Name = "labelMiscellaneous";
this.labelMiscellaneous.Size = new System.Drawing.Size(74, 13);
this.labelMiscellaneous.TabIndex = 13;
this.labelMiscellaneous.Text = "Miscellaneous";
//
// checkHardwareAcceleration
//
this.checkHardwareAcceleration.AutoSize = true;
this.checkHardwareAcceleration.Location = new System.Drawing.Point(9, 9);
this.checkHardwareAcceleration.Name = "checkHardwareAcceleration";
this.checkHardwareAcceleration.Size = new System.Drawing.Size(134, 17);
this.checkHardwareAcceleration.TabIndex = 12;
this.checkHardwareAcceleration.Text = "Hardware Acceleration";
this.checkHardwareAcceleration.UseVisualStyleBackColor = true;
this.checkHardwareAcceleration.CheckedChanged += new System.EventHandler(this.checkHardwareAcceleration_CheckedChanged);
//
// TabSettingsAdvanced
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.btnClearCache);
this.Controls.Add(this.labelMiscellaneous);
this.Controls.Add(this.checkHardwareAcceleration);
this.Name = "TabSettingsAdvanced";
this.Size = new System.Drawing.Size(239, 120);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnClearCache;
private System.Windows.Forms.Label labelMiscellaneous;
private System.Windows.Forms.CheckBox checkHardwareAcceleration;
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Diagnostics;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsAdvanced : BaseTabSettings{
public TabSettingsAdvanced(){
InitializeComponent();
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
BrowserCache.CalculateCacheSize(bytes => this.InvokeSafe(() => {
if (bytes == -1L){
btnClearCache.Text = "Clear Cache (unknown size)";
}
else{
btnClearCache.Text = "Clear Cache ("+(int)Math.Ceiling(bytes/(1024.0*1024.0))+" MB)";
}
}));
}
private void btnClearCache_Click(object sender, EventArgs e){
if (!Ready)return;
btnClearCache.Enabled = false;
BrowserCache.SetClearOnExit();
MessageBox.Show("Cache will be automatically cleared when "+Program.BrandName+" exits.","Clear Cache",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
private void checkHardwareAcceleration_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
bool succeeded = false;
if (checkHardwareAcceleration.Checked){
if (HardwareAcceleration.CanEnable){
succeeded = HardwareAcceleration.Enable();
}
else{
MessageBox.Show("Cannot enable hardware acceleration, the libraries libEGL.dll and libGLESv2.dll could not be restored.",Program.BrandName+" Settings",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
}
else{
succeeded = HardwareAcceleration.Disable();
}
if (succeeded && 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){ // TODO
Process.Start(Application.ExecutablePath,"-restart");
Application.Exit();
}
else if (!succeeded){
checkHardwareAcceleration.CheckedChanged -= checkHardwareAcceleration_CheckedChanged;
checkHardwareAcceleration.Checked = HardwareAcceleration.IsEnabled;
checkHardwareAcceleration.CheckedChanged += checkHardwareAcceleration_CheckedChanged;
}
}
}
}

View File

@@ -0,0 +1,82 @@
namespace TweetDck.Core.Other.Settings {
partial class TabSettingsGeneral {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.checkExpandLinks = new System.Windows.Forms.CheckBox();
this.comboBoxTrayType = new System.Windows.Forms.ComboBox();
this.labelTrayType = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// checkExpandLinks
//
this.checkExpandLinks.AutoSize = true;
this.checkExpandLinks.Location = new System.Drawing.Point(9, 9);
this.checkExpandLinks.Name = "checkExpandLinks";
this.checkExpandLinks.Size = new System.Drawing.Size(166, 17);
this.checkExpandLinks.TabIndex = 14;
this.checkExpandLinks.Text = "Expand Links When Hovered";
this.checkExpandLinks.UseVisualStyleBackColor = true;
this.checkExpandLinks.CheckedChanged += new System.EventHandler(this.checkExpandLinks_CheckedChanged);
//
// comboBoxTrayType
//
this.comboBoxTrayType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxTrayType.FormattingEnabled = true;
this.comboBoxTrayType.Location = new System.Drawing.Point(9, 56);
this.comboBoxTrayType.Name = "comboBoxTrayType";
this.comboBoxTrayType.Size = new System.Drawing.Size(171, 21);
this.comboBoxTrayType.TabIndex = 13;
this.comboBoxTrayType.SelectedIndexChanged += new System.EventHandler(this.comboBoxTrayType_SelectedIndexChanged);
//
// labelTrayType
//
this.labelTrayType.AutoSize = true;
this.labelTrayType.Location = new System.Drawing.Point(6, 40);
this.labelTrayType.Margin = new System.Windows.Forms.Padding(3, 11, 3, 0);
this.labelTrayType.Name = "labelTrayType";
this.labelTrayType.Size = new System.Drawing.Size(52, 13);
this.labelTrayType.TabIndex = 12;
this.labelTrayType.Text = "Tray Icon";
//
// TabSettingsGeneral
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.checkExpandLinks);
this.Controls.Add(this.comboBoxTrayType);
this.Controls.Add(this.labelTrayType);
this.Name = "TabSettingsGeneral";
this.Size = new System.Drawing.Size(219, 99);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckBox checkExpandLinks;
private System.Windows.Forms.ComboBox comboBoxTrayType;
private System.Windows.Forms.Label labelTrayType;
}
}

View File

@@ -0,0 +1,29 @@
using System;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsGeneral : BaseTabSettings{
public TabSettingsGeneral(){
InitializeComponent();
comboBoxTrayType.Items.Add("Disabled");
comboBoxTrayType.Items.Add("Display Icon Only");
comboBoxTrayType.Items.Add("Minimize to Tray");
comboBoxTrayType.Items.Add("Close to Tray");
comboBoxTrayType.SelectedIndex = Math.Min(Math.Max((int)Config.TrayBehavior,0),comboBoxTrayType.Items.Count-1);
checkExpandLinks.Checked = Program.UserConfig.ExpandLinksOnHover;
}
private void checkExpandLinks_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.ExpandLinksOnHover = checkExpandLinks.Checked;
}
private void comboBoxTrayType_SelectedIndexChanged(object sender, EventArgs e){
if (!Ready)return;
Config.TrayBehavior = (TrayIcon.Behavior)comboBoxTrayType.SelectedIndex;
}
}
}

View File

@@ -0,0 +1,303 @@
namespace TweetDck.Core.Other.Settings {
partial class TabSettingsNotifications {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.groupNotificationLocation = new System.Windows.Forms.GroupBox();
this.labelDisplay = new System.Windows.Forms.Label();
this.comboBoxDisplay = new System.Windows.Forms.ComboBox();
this.labelEdgeDistance = new System.Windows.Forms.Label();
this.radioLocCustom = new System.Windows.Forms.RadioButton();
this.radioLocBR = new System.Windows.Forms.RadioButton();
this.radioLocBL = new System.Windows.Forms.RadioButton();
this.radioLocTR = new System.Windows.Forms.RadioButton();
this.radioLocTL = new System.Windows.Forms.RadioButton();
this.trackBarEdgeDistance = new System.Windows.Forms.TrackBar();
this.groupNotificationDuration = new System.Windows.Forms.GroupBox();
this.radioDurVeryLong = new System.Windows.Forms.RadioButton();
this.radioDurLong = new System.Windows.Forms.RadioButton();
this.radioDurMedium = new System.Windows.Forms.RadioButton();
this.radioDurShort = new System.Windows.Forms.RadioButton();
this.groupUserInterface = new System.Windows.Forms.GroupBox();
this.checkNotificationTimer = new System.Windows.Forms.CheckBox();
this.groupNotificationLocation.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).BeginInit();
this.groupNotificationDuration.SuspendLayout();
this.groupUserInterface.SuspendLayout();
this.SuspendLayout();
//
// groupNotificationLocation
//
this.groupNotificationLocation.Controls.Add(this.labelDisplay);
this.groupNotificationLocation.Controls.Add(this.comboBoxDisplay);
this.groupNotificationLocation.Controls.Add(this.labelEdgeDistance);
this.groupNotificationLocation.Controls.Add(this.radioLocCustom);
this.groupNotificationLocation.Controls.Add(this.radioLocBR);
this.groupNotificationLocation.Controls.Add(this.radioLocBL);
this.groupNotificationLocation.Controls.Add(this.radioLocTR);
this.groupNotificationLocation.Controls.Add(this.radioLocTL);
this.groupNotificationLocation.Controls.Add(this.trackBarEdgeDistance);
this.groupNotificationLocation.Location = new System.Drawing.Point(9, 9);
this.groupNotificationLocation.Name = "groupNotificationLocation";
this.groupNotificationLocation.Size = new System.Drawing.Size(183, 264);
this.groupNotificationLocation.TabIndex = 1;
this.groupNotificationLocation.TabStop = false;
this.groupNotificationLocation.Text = "Notification Location";
//
// labelDisplay
//
this.labelDisplay.AutoSize = true;
this.labelDisplay.Location = new System.Drawing.Point(3, 148);
this.labelDisplay.Margin = new System.Windows.Forms.Padding(3, 12, 3, 0);
this.labelDisplay.Name = "labelDisplay";
this.labelDisplay.Size = new System.Drawing.Size(41, 13);
this.labelDisplay.TabIndex = 8;
this.labelDisplay.Text = "Display";
//
// comboBoxDisplay
//
this.comboBoxDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.comboBoxDisplay.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.comboBoxDisplay.FormattingEnabled = true;
this.comboBoxDisplay.Location = new System.Drawing.Point(6, 164);
this.comboBoxDisplay.Name = "comboBoxDisplay";
this.comboBoxDisplay.Size = new System.Drawing.Size(171, 21);
this.comboBoxDisplay.TabIndex = 7;
this.comboBoxDisplay.SelectedValueChanged += new System.EventHandler(this.comboBoxDisplay_SelectedValueChanged);
//
// labelEdgeDistance
//
this.labelEdgeDistance.AutoSize = true;
this.labelEdgeDistance.Location = new System.Drawing.Point(3, 197);
this.labelEdgeDistance.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelEdgeDistance.Name = "labelEdgeDistance";
this.labelEdgeDistance.Size = new System.Drawing.Size(103, 13);
this.labelEdgeDistance.TabIndex = 6;
this.labelEdgeDistance.Text = "Distance From Edge";
//
// radioLocCustom
//
this.radioLocCustom.AutoSize = true;
this.radioLocCustom.Location = new System.Drawing.Point(7, 116);
this.radioLocCustom.Name = "radioLocCustom";
this.radioLocCustom.Size = new System.Drawing.Size(60, 17);
this.radioLocCustom.TabIndex = 4;
this.radioLocCustom.TabStop = true;
this.radioLocCustom.Text = "Custom";
this.radioLocCustom.UseVisualStyleBackColor = true;
this.radioLocCustom.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
//
// radioLocBR
//
this.radioLocBR.AutoSize = true;
this.radioLocBR.Location = new System.Drawing.Point(7, 92);
this.radioLocBR.Name = "radioLocBR";
this.radioLocBR.Size = new System.Drawing.Size(86, 17);
this.radioLocBR.TabIndex = 3;
this.radioLocBR.TabStop = true;
this.radioLocBR.Text = "Bottom Right";
this.radioLocBR.UseVisualStyleBackColor = true;
this.radioLocBR.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
//
// radioLocBL
//
this.radioLocBL.AutoSize = true;
this.radioLocBL.Location = new System.Drawing.Point(7, 68);
this.radioLocBL.Name = "radioLocBL";
this.radioLocBL.Size = new System.Drawing.Size(79, 17);
this.radioLocBL.TabIndex = 2;
this.radioLocBL.TabStop = true;
this.radioLocBL.Text = "Bottom Left";
this.radioLocBL.UseVisualStyleBackColor = true;
this.radioLocBL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
//
// radioLocTR
//
this.radioLocTR.AutoSize = true;
this.radioLocTR.Location = new System.Drawing.Point(7, 44);
this.radioLocTR.Name = "radioLocTR";
this.radioLocTR.Size = new System.Drawing.Size(72, 17);
this.radioLocTR.TabIndex = 1;
this.radioLocTR.TabStop = true;
this.radioLocTR.Text = "Top Right";
this.radioLocTR.UseVisualStyleBackColor = true;
this.radioLocTR.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
//
// radioLocTL
//
this.radioLocTL.AutoSize = true;
this.radioLocTL.Location = new System.Drawing.Point(7, 20);
this.radioLocTL.Name = "radioLocTL";
this.radioLocTL.Size = new System.Drawing.Size(65, 17);
this.radioLocTL.TabIndex = 0;
this.radioLocTL.TabStop = true;
this.radioLocTL.Text = "Top Left";
this.radioLocTL.UseVisualStyleBackColor = true;
this.radioLocTL.CheckedChanged += new System.EventHandler(this.radioLoc_CheckedChanged);
//
// trackBarEdgeDistance
//
this.trackBarEdgeDistance.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBarEdgeDistance.LargeChange = 8;
this.trackBarEdgeDistance.Location = new System.Drawing.Point(6, 213);
this.trackBarEdgeDistance.Maximum = 40;
this.trackBarEdgeDistance.Minimum = 8;
this.trackBarEdgeDistance.Name = "trackBarEdgeDistance";
this.trackBarEdgeDistance.Size = new System.Drawing.Size(171, 45);
this.trackBarEdgeDistance.SmallChange = 2;
this.trackBarEdgeDistance.TabIndex = 5;
this.trackBarEdgeDistance.TickFrequency = 2;
this.trackBarEdgeDistance.Value = 8;
this.trackBarEdgeDistance.ValueChanged += new System.EventHandler(this.trackBarEdgeDistance_ValueChanged);
//
// groupNotificationDuration
//
this.groupNotificationDuration.Controls.Add(this.radioDurVeryLong);
this.groupNotificationDuration.Controls.Add(this.radioDurLong);
this.groupNotificationDuration.Controls.Add(this.radioDurMedium);
this.groupNotificationDuration.Controls.Add(this.radioDurShort);
this.groupNotificationDuration.Location = new System.Drawing.Point(198, 9);
this.groupNotificationDuration.Name = "groupNotificationDuration";
this.groupNotificationDuration.Size = new System.Drawing.Size(183, 119);
this.groupNotificationDuration.TabIndex = 9;
this.groupNotificationDuration.TabStop = false;
this.groupNotificationDuration.Text = "Notification Duration";
//
// radioDurVeryLong
//
this.radioDurVeryLong.AutoSize = true;
this.radioDurVeryLong.Location = new System.Drawing.Point(6, 92);
this.radioDurVeryLong.Name = "radioDurVeryLong";
this.radioDurVeryLong.Size = new System.Drawing.Size(73, 17);
this.radioDurVeryLong.TabIndex = 3;
this.radioDurVeryLong.TabStop = true;
this.radioDurVeryLong.Text = "Very Long";
this.radioDurVeryLong.UseVisualStyleBackColor = true;
this.radioDurVeryLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurVeryLong.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurLong
//
this.radioDurLong.AutoSize = true;
this.radioDurLong.Location = new System.Drawing.Point(6, 68);
this.radioDurLong.Name = "radioDurLong";
this.radioDurLong.Size = new System.Drawing.Size(49, 17);
this.radioDurLong.TabIndex = 2;
this.radioDurLong.TabStop = true;
this.radioDurLong.Text = "Long";
this.radioDurLong.UseVisualStyleBackColor = true;
this.radioDurLong.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurLong.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurMedium
//
this.radioDurMedium.AutoSize = true;
this.radioDurMedium.Location = new System.Drawing.Point(6, 44);
this.radioDurMedium.Name = "radioDurMedium";
this.radioDurMedium.Size = new System.Drawing.Size(62, 17);
this.radioDurMedium.TabIndex = 1;
this.radioDurMedium.TabStop = true;
this.radioDurMedium.Text = "Medium";
this.radioDurMedium.UseVisualStyleBackColor = true;
this.radioDurMedium.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurMedium.Click += new System.EventHandler(this.radioDur_Click);
//
// radioDurShort
//
this.radioDurShort.AutoSize = true;
this.radioDurShort.Location = new System.Drawing.Point(6, 20);
this.radioDurShort.Name = "radioDurShort";
this.radioDurShort.Size = new System.Drawing.Size(50, 17);
this.radioDurShort.TabIndex = 0;
this.radioDurShort.TabStop = true;
this.radioDurShort.Text = "Short";
this.radioDurShort.UseVisualStyleBackColor = true;
this.radioDurShort.CheckedChanged += new System.EventHandler(this.radioDur_CheckedChanged);
this.radioDurShort.Click += new System.EventHandler(this.radioDur_Click);
//
// groupUserInterface
//
this.groupUserInterface.Controls.Add(this.checkNotificationTimer);
this.groupUserInterface.Location = new System.Drawing.Point(198, 134);
this.groupUserInterface.Name = "groupUserInterface";
this.groupUserInterface.Size = new System.Drawing.Size(183, 48);
this.groupUserInterface.TabIndex = 10;
this.groupUserInterface.TabStop = false;
this.groupUserInterface.Text = "User Interface";
//
// checkNotificationTimer
//
this.checkNotificationTimer.AutoSize = true;
this.checkNotificationTimer.Location = new System.Drawing.Point(6, 21);
this.checkNotificationTimer.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3);
this.checkNotificationTimer.Name = "checkNotificationTimer";
this.checkNotificationTimer.Size = new System.Drawing.Size(145, 17);
this.checkNotificationTimer.TabIndex = 4;
this.checkNotificationTimer.Text = "Display Notification Timer";
this.checkNotificationTimer.UseVisualStyleBackColor = true;
this.checkNotificationTimer.CheckedChanged += new System.EventHandler(this.checkNotificationTimer_CheckedChanged);
//
// TabSettingsNotifications
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.groupUserInterface);
this.Controls.Add(this.groupNotificationDuration);
this.Controls.Add(this.groupNotificationLocation);
this.Name = "TabSettingsNotifications";
this.Size = new System.Drawing.Size(397, 282);
this.ParentChanged += new System.EventHandler(this.TabSettingsNotifications_ParentChanged);
this.groupNotificationLocation.ResumeLayout(false);
this.groupNotificationLocation.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBarEdgeDistance)).EndInit();
this.groupNotificationDuration.ResumeLayout(false);
this.groupNotificationDuration.PerformLayout();
this.groupUserInterface.ResumeLayout(false);
this.groupUserInterface.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox groupNotificationLocation;
private System.Windows.Forms.Label labelDisplay;
private System.Windows.Forms.ComboBox comboBoxDisplay;
private System.Windows.Forms.Label labelEdgeDistance;
private System.Windows.Forms.TrackBar trackBarEdgeDistance;
private System.Windows.Forms.RadioButton radioLocCustom;
private System.Windows.Forms.RadioButton radioLocBR;
private System.Windows.Forms.RadioButton radioLocBL;
private System.Windows.Forms.RadioButton radioLocTR;
private System.Windows.Forms.RadioButton radioLocTL;
private System.Windows.Forms.GroupBox groupNotificationDuration;
private System.Windows.Forms.RadioButton radioDurVeryLong;
private System.Windows.Forms.RadioButton radioDurLong;
private System.Windows.Forms.RadioButton radioDurMedium;
private System.Windows.Forms.RadioButton radioDurShort;
private System.Windows.Forms.GroupBox groupUserInterface;
private System.Windows.Forms.CheckBox checkNotificationTimer;
}
}

View File

@@ -0,0 +1,120 @@
using System;
using System.Windows.Forms;
using TweetDck.Core.Handling;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsNotifications : BaseTabSettings{
private readonly FormNotification notification;
public TabSettingsNotifications(FormNotification notification){
InitializeComponent();
this.notification = notification;
this.notification.CanMoveWindow = () => radioLocCustom.Checked;
this.notification.Move += (sender, args) => {
if (radioLocCustom.Checked){
Config.CustomNotificationPosition = this.notification.Location;
}
};
this.notification.Initialized += (sender, args) => {
this.notification.ShowNotificationForSettings(true);
};
this.notification.Show(this);
switch(Config.NotificationPosition){
case TweetNotification.Position.TopLeft: radioLocTL.Checked = true; break;
case TweetNotification.Position.TopRight: radioLocTR.Checked = true; break;
case TweetNotification.Position.BottomLeft: radioLocBL.Checked = true; break;
case TweetNotification.Position.BottomRight: radioLocBR.Checked = true; break;
case TweetNotification.Position.Custom: radioLocCustom.Checked = true; break;
}
switch(Config.NotificationDuration){
case TweetNotification.Duration.Short: radioDurShort.Checked = true; break;
case TweetNotification.Duration.Medium: radioDurMedium.Checked = true; break;
case TweetNotification.Duration.Long: radioDurLong.Checked = true; break;
case TweetNotification.Duration.VeryLong: radioDurVeryLong.Checked = true; break;
}
comboBoxDisplay.Items.Add("(Same As "+Program.BrandName+")");
foreach(Screen screen in Screen.AllScreens){
comboBoxDisplay.Items.Add(screen.DeviceName+" ("+screen.Bounds.Width+"x"+screen.Bounds.Height+")");
}
comboBoxDisplay.SelectedIndex = Math.Min(comboBoxDisplay.Items.Count-1,Config.NotificationDisplay);
checkNotificationTimer.Checked = Config.DisplayNotificationTimer;
trackBarEdgeDistance.Value = Config.NotificationEdgeDistance;
}
private void TabSettingsNotifications_ParentChanged(object sender, EventArgs e){
if (Parent == null){
notification.HideNotification(false);
}
else{
notification.ShowNotificationForSettings(false);
}
}
private void radioLoc_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
if (radioLocTL.Checked)Config.NotificationPosition = TweetNotification.Position.TopLeft;
else if (radioLocTR.Checked)Config.NotificationPosition = TweetNotification.Position.TopRight;
else if (radioLocBL.Checked)Config.NotificationPosition = TweetNotification.Position.BottomLeft;
else if (radioLocBR.Checked)Config.NotificationPosition = TweetNotification.Position.BottomRight;
else if (radioLocCustom.Checked){
if (!Config.IsCustomNotificationPositionSet){
Config.CustomNotificationPosition = notification.Location;
}
Config.NotificationPosition = TweetNotification.Position.Custom;
}
comboBoxDisplay.Enabled = trackBarEdgeDistance.Enabled = !radioLocCustom.Checked;
notification.ShowNotificationForSettings(false);
}
private void radioDur_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
if (radioDurShort.Checked)Config.NotificationDuration = TweetNotification.Duration.Short;
else if (radioDurMedium.Checked)Config.NotificationDuration = TweetNotification.Duration.Medium;
else if (radioDurLong.Checked)Config.NotificationDuration = TweetNotification.Duration.Long;
else if (radioDurVeryLong.Checked)Config.NotificationDuration = TweetNotification.Duration.VeryLong;
notification.ShowNotificationForSettings(true);
}
private void radioDur_Click(object sender, EventArgs e){
if (!Ready)return;
notification.ShowNotificationForSettings(true);
}
private void checkNotificationTimer_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.DisplayNotificationTimer = checkNotificationTimer.Checked;
notification.ShowNotificationForSettings(true);
}
private void comboBoxDisplay_SelectedValueChanged(object sender, EventArgs e){
if (!Ready)return;
Config.NotificationDisplay = comboBoxDisplay.SelectedIndex;
notification.ShowNotificationForSettings(false);
}
private void trackBarEdgeDistance_ValueChanged(object sender, EventArgs e){
if (!Ready)return;
Config.NotificationEdgeDistance = trackBarEdgeDistance.Value;
notification.ShowNotificationForSettings(false);
}
}
}

View File

@@ -0,0 +1,69 @@
namespace TweetDck.Core.Other.Settings {
partial class TabSettingsUpdates {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.btnCheckUpdates = new System.Windows.Forms.Button();
this.checkUpdateNotifications = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// btnCheckUpdates
//
this.btnCheckUpdates.Location = new System.Drawing.Point(9, 32);
this.btnCheckUpdates.Name = "btnCheckUpdates";
this.btnCheckUpdates.Size = new System.Drawing.Size(171, 23);
this.btnCheckUpdates.TabIndex = 15;
this.btnCheckUpdates.Text = "Check Updates Now";
this.btnCheckUpdates.UseVisualStyleBackColor = true;
this.btnCheckUpdates.Click += new System.EventHandler(this.btnCheckUpdates_Click);
//
// checkUpdateNotifications
//
this.checkUpdateNotifications.AutoSize = true;
this.checkUpdateNotifications.Location = new System.Drawing.Point(9, 9);
this.checkUpdateNotifications.Name = "checkUpdateNotifications";
this.checkUpdateNotifications.Size = new System.Drawing.Size(165, 17);
this.checkUpdateNotifications.TabIndex = 14;
this.checkUpdateNotifications.Text = "Check Updates Automatically";
this.checkUpdateNotifications.UseVisualStyleBackColor = true;
this.checkUpdateNotifications.CheckedChanged += new System.EventHandler(this.checkUpdateNotifications_CheckedChanged);
//
// TabSettingsUpdates
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.btnCheckUpdates);
this.Controls.Add(this.checkUpdateNotifications);
this.Name = "TabSettingsUpdates";
this.Size = new System.Drawing.Size(211, 78);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnCheckUpdates;
private System.Windows.Forms.CheckBox checkUpdateNotifications;
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Windows.Forms;
using TweetDck.Updates;
namespace TweetDck.Core.Other.Settings{
partial class TabSettingsUpdates : BaseTabSettings{
private readonly UpdateHandler updates;
private int updateCheckEventId = -1;
public TabSettingsUpdates(UpdateHandler updates){
InitializeComponent();
this.updates = updates;
this.updates.CheckFinished += updates_CheckFinished;
Disposed += (sender, args) => this.updates.CheckFinished -= updates_CheckFinished;
checkUpdateNotifications.Checked = Config.EnableUpdateCheck;
}
private void checkUpdateNotifications_CheckedChanged(object sender, EventArgs e){
if (!Ready)return;
Config.EnableUpdateCheck = checkUpdateNotifications.Checked;
}
private void btnCheckUpdates_Click(object sender, EventArgs e){
if (!Ready)return;
Config.DismissedUpdate = string.Empty;
Config.Save();
updateCheckEventId = updates.Check(true);
btnCheckUpdates.Enabled = false;
}
private void updates_CheckFinished(object sender, UpdateCheckEventArgs e){
if (e.EventId == updateCheckEventId){
btnCheckUpdates.Enabled = true;
if (!e.UpdateAvailable){
MessageBox.Show("Your version of "+Program.BrandName+" is up to date.","No Updates Available",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
}
}
}
}

View File

@@ -31,13 +31,13 @@
this.closeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.contextMenu.SuspendLayout();
//
// trayIcon
// notifyIcon
//
this.notifyIcon.ContextMenuStrip = this.contextMenu;
this.notifyIcon.Icon = global::TweetDck.Properties.Resources.icon;
this.notifyIcon.Icon = global::TweetDck.Properties.Resources.icon_tray;
this.notifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(this.trayIcon_MouseClick);
//
// contextMenuTray
// contextMenu
//
this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.restoreToolStripMenuItem,

View File

@@ -21,6 +21,21 @@ namespace TweetDck.Core{
}
}
public bool HasNotifications{
get{
return hasNotifications;
}
set{
if (hasNotifications != value){
notifyIcon.Icon = value ? Properties.Resources.icon_tray_new : Properties.Resources.icon_tray;
hasNotifications = value;
}
}
}
private bool hasNotifications;
public TrayIcon(){
InitializeComponent();
notifyIcon.Text = Program.BrandName;

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Linq;
namespace TweetDck.Core.Utils{
static class BrowserCache{
private static bool ClearOnExit { get; set; }
private static readonly string IndexFile = Path.Combine(Program.StoragePath,"index");
private static IEnumerable<string> CacheFiles{
get{
return Directory.EnumerateFiles(Program.StoragePath).Where(path => {
string file = Path.GetFileName(path);
return file != null && (file.StartsWith("data_",StringComparison.Ordinal) || file.StartsWith("f_",StringComparison.Ordinal));
}).Concat(new[]{ IndexFile });
}
}
public static void CalculateCacheSize(Action<long> callbackBytes){
Task<long> task = new Task<long>(() => {
return CacheFiles.Select(file => {
try{
return new FileInfo(file).Length;
}catch{
return 0L;
}
}).Sum();
});
task.ContinueWith(originalTask => callbackBytes(originalTask.Exception == null ? originalTask.Result : -1L),TaskContinuationOptions.ExecuteSynchronously);
task.Start();
}
public static void SetClearOnExit(){
ClearOnExit = true;
}
public static void Exit(){
if (ClearOnExit){
foreach(string file in CacheFiles){
try{
File.Delete(file);
}catch{
// welp, too bad
}
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.IO;
namespace TweetDck.Core.Utils{
static class HardwareAcceleration{
private static readonly string LibEGL = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"libEGL.dll");
private static readonly string LibGLES = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"libGLESv2.dll");
private static readonly string DisabledLibEGL = LibEGL+".bak";
private static readonly string DisabledLibGLES = LibGLES+".bak";
public static bool IsEnabled{
get{
return File.Exists(LibEGL) && File.Exists(LibGLES);
}
}
public static bool CanEnable{
get{
return File.Exists(DisabledLibEGL) && File.Exists(DisabledLibGLES);
}
}
public static bool Enable(){
if (IsEnabled)return false;
try{
File.Move(DisabledLibEGL,LibEGL);
File.Move(DisabledLibGLES,LibGLES);
return true;
}catch{
return false;
}
}
public static bool Disable(){
if (!IsEnabled)return false;
try{
if (File.Exists(DisabledLibEGL)){
File.Delete(DisabledLibEGL);
}
if (File.Exists(DisabledLibGLES)){
File.Delete(DisabledLibGLES);
}
}catch{
// woops
}
try{
File.Move(LibEGL,DisabledLibEGL);
File.Move(LibGLES,DisabledLibGLES);
return true;
}catch{
return false;
}
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TweetDck.Core.Utils{
static class NativeMethods{
public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
public const int HWND_TOPMOST = -1;
public const uint SWP_NOACTIVATE = 0x0010;
public const int MOUSEEVENTF_LEFTDOWN = 0x02;
public const int MOUSEEVENTF_LEFTUP = 0x04;
public const int MOUSEEVENTF_RIGHTDOWN = 0x08;
public const int MOUSEEVENTF_RIGHTUP = 0x10;
public const int SB_HORZ = 0;
public enum MouseButton{
Left, Right
}
[DllImport("Shell32.dll")]
public static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint RegisterWindowMessage(string messageName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
public static void SetFormPos(Form form, int hWndOrder, uint flags){
SetWindowPos(form.Handle.ToInt32(),hWndOrder,form.Left,form.Top,form.Width,form.Height,flags);
}
public static void SimulateMouseClick(MouseButton button){
int flagHold, flagRelease;
switch(button){
case MouseButton.Left:
flagHold = SystemInformation.MouseButtonsSwapped ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN;
flagRelease = SystemInformation.MouseButtonsSwapped ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP;
break;
case MouseButton.Right:
flagHold = SystemInformation.MouseButtonsSwapped ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN;
flagRelease = SystemInformation.MouseButtonsSwapped ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP;
break;
default: return;
}
mouse_event(flagHold,Cursor.Position.X,Cursor.Position.Y,0,0);
mouse_event(flagRelease,Cursor.Position.X,Cursor.Position.Y,0,0);
}
}
}

30
Core/Utils/WindowState.cs Normal file
View File

@@ -0,0 +1,30 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace TweetDck.Core.Utils{
[Serializable]
class WindowState{
private Rectangle rect;
private bool isMaximized;
public void Save(Form form){
rect = form.WindowState == FormWindowState.Normal ? form.DesktopBounds : form.RestoreBounds;
isMaximized = form.WindowState == FormWindowState.Maximized;
}
public void Restore(Form form, bool firstTimeFullscreen){
if (rect != Rectangle.Empty){
form.DesktopBounds = rect;
form.WindowState = isMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
if ((rect == Rectangle.Empty && firstTimeFullscreen) || !Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(form.Bounds))){
form.DesktopBounds = Screen.PrimaryScreen.WorkingArea;
form.WindowState = FormWindowState.Maximized;
Save(form);
}
}
}
}

View File

@@ -1,6 +1,6 @@
using TweetDck.Core.Controls;
namespace TweetDck.Core.Other {
namespace TweetDck.Migration {
partial class FormBackgroundWork {
/// <summary>
/// Required designer variable.
@@ -26,17 +26,17 @@ namespace TweetDck.Core.Other {
/// </summary>
private void InitializeComponent() {
this.progressBarUseless = new System.Windows.Forms.ProgressBar();
this.labelDescription = new RichTextLabel();
this.labelDescription = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// progressBarUseless
//
this.progressBarUseless.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.progressBarUseless.Location = new System.Drawing.Point(12, 49);
this.progressBarUseless.Location = new System.Drawing.Point(15, 52);
this.progressBarUseless.MarqueeAnimationSpeed = 10;
this.progressBarUseless.Name = "progressBarUseless";
this.progressBarUseless.Size = new System.Drawing.Size(480, 23);
this.progressBarUseless.Size = new System.Drawing.Size(477, 23);
this.progressBarUseless.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressBarUseless.TabIndex = 0;
//
@@ -45,25 +45,24 @@ namespace TweetDck.Core.Other {
this.labelDescription.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.labelDescription.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.labelDescription.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelDescription.Location = new System.Drawing.Point(12, 12);
this.labelDescription.Name = "labelDescription";
this.labelDescription.ReadOnly = true;
this.labelDescription.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this.labelDescription.Size = new System.Drawing.Size(480, 31);
this.labelDescription.Size = new System.Drawing.Size(480, 37);
this.labelDescription.TabIndex = 1;
this.labelDescription.TabStop = false;
this.labelDescription.Text = "";
this.labelDescription.Text = "Please, watch this informationless progress bar showcase while some magic happens" +
" in the background...";
//
// FormBackgroundWork
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(504, 84);
this.ClientSize = new System.Drawing.Size(504, 87);
this.Controls.Add(this.labelDescription);
this.Controls.Add(this.progressBarUseless);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Name = "FormBackgroundWork";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "TweetDeck Migration";
this.ResumeLayout(false);
@@ -73,6 +72,6 @@ namespace TweetDck.Core.Other {
#endregion
private System.Windows.Forms.ProgressBar progressBarUseless;
private RichTextLabel labelDescription;
private System.Windows.Forms.Label labelDescription;
}
}

View File

@@ -1,13 +1,10 @@
using System;
using System.Windows.Forms;
using TweetDck.Core.Controls;
namespace TweetDck.Core.Other{
namespace TweetDck.Migration{
partial class FormBackgroundWork : Form{
public FormBackgroundWork(){
InitializeComponent();
labelDescription.Rtf = RichTextLabel.Wrap(@"Please, watch this informationless progress bar showcase while some magic happens in the background...");
}
public void ShowWorkDialog(Action onBegin){

View File

@@ -28,10 +28,9 @@ namespace TweetDck.Migration {
this.btnIgnore = new System.Windows.Forms.Button();
this.panelButtons = new System.Windows.Forms.FlowLayoutPanel();
this.btnAskLater = new System.Windows.Forms.Button();
this.btnCopy = new System.Windows.Forms.Button();
this.btnMigrate = new System.Windows.Forms.Button();
this.btnMigrateUninstall = new System.Windows.Forms.Button();
this.labelQuestion = new RichTextLabel();
this.labelQuestion = new System.Windows.Forms.Label();
this.panelButtons.SuspendLayout();
this.SuspendLayout();
//
@@ -39,8 +38,8 @@ namespace TweetDck.Migration {
//
this.btnIgnore.AutoSize = true;
this.btnIgnore.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnIgnore.Location = new System.Drawing.Point(356, 0);
this.btnIgnore.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0);
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);
@@ -55,20 +54,19 @@ namespace TweetDck.Migration {
| System.Windows.Forms.AnchorStyles.Right)));
this.panelButtons.Controls.Add(this.btnAskLater);
this.panelButtons.Controls.Add(this.btnIgnore);
this.panelButtons.Controls.Add(this.btnCopy);
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, 87);
this.panelButtons.Location = new System.Drawing.Point(12, 67);
this.panelButtons.Name = "panelButtons";
this.panelButtons.Size = new System.Drawing.Size(480, 23);
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(412, 0);
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);
@@ -78,25 +76,11 @@ namespace TweetDck.Migration {
this.btnAskLater.UseVisualStyleBackColor = true;
this.btnAskLater.Click += new System.EventHandler(this.btnAskLater_Click);
//
// btnCopy
//
this.btnCopy.AutoSize = true;
this.btnCopy.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnCopy.Location = new System.Drawing.Point(303, 0);
this.btnCopy.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCopy.Name = "btnCopy";
this.btnCopy.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.btnCopy.Size = new System.Drawing.Size(47, 23);
this.btnCopy.TabIndex = 2;
this.btnCopy.Text = "Copy";
this.btnCopy.UseVisualStyleBackColor = true;
this.btnCopy.Click += new System.EventHandler(this.btnCopy_Click);
//
// btnMigrate
//
this.btnMigrate.AutoSize = true;
this.btnMigrate.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnMigrate.Location = new System.Drawing.Point(239, 0);
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);
@@ -110,7 +94,7 @@ namespace TweetDck.Migration {
//
this.btnMigrateUninstall.AutoSize = true;
this.btnMigrateUninstall.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.btnMigrateUninstall.Location = new System.Drawing.Point(135, 0);
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);
@@ -125,28 +109,25 @@ namespace TweetDck.Migration {
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.BorderStyle = System.Windows.Forms.BorderStyle.None;
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.ReadOnly = true;
this.labelQuestion.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this.labelQuestion.Size = new System.Drawing.Size(443, 72);
this.labelQuestion.Size = new System.Drawing.Size(481, 52);
this.labelQuestion.TabIndex = 2;
this.labelQuestion.TabStop = false;
this.labelQuestion.Text = "";
//
// FormMigrationQuestion
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(504, 122);
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);
@@ -159,9 +140,8 @@ namespace TweetDck.Migration {
private System.Windows.Forms.Button btnIgnore;
private System.Windows.Forms.FlowLayoutPanel panelButtons;
private System.Windows.Forms.Button btnCopy;
private System.Windows.Forms.Button btnMigrate;
private RichTextLabel labelQuestion;
private System.Windows.Forms.Label labelQuestion;
private System.Windows.Forms.Button btnAskLater;
private System.Windows.Forms.Button btnMigrateUninstall;
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using TweetDck.Core.Controls;
namespace TweetDck.Migration{
partial class FormMigrationQuestion : Form{
@@ -10,7 +9,7 @@ namespace TweetDck.Migration{
public FormMigrationQuestion(){
InitializeComponent();
labelQuestion.Rtf = RichTextLabel.Wrap(@"Hey there, I found some TweetDeck data! Do you want to \b Migrate\b0 it and delete the old data folder, \b Copy\b0 it and keep the folder, \b Ignore\b0 the request forever, or do you need some more time for the decision?\par You may also \b Migrate & Purge\b0 which uninstalls TweetDeck too!");
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){
@@ -26,10 +25,6 @@ namespace TweetDck.Migration{
Close(MigrationDecision.Migrate);
}
private void btnCopy_Click(object sender, EventArgs e){
Close(MigrationDecision.Copy);
}
private void btnIgnore_Click(object sender, EventArgs e){
Close(MigrationDecision.Ignore);
}

View File

@@ -1,15 +0,0 @@
using System;
using System.Diagnostics;
using System.Linq;
namespace TweetDck.Migration.Helpers{
static class ProgramProcessSearch{
public static Process FindProcessWithWindowByName(string name){
try{
return Process.GetProcessesByName(name).FirstOrDefault(process => process.MainWindowHandle != IntPtr.Zero);
}catch(Exception){
return null;
}
}
}
}

View File

@@ -10,11 +10,6 @@
/// </summary>
MigratePurge,
/// <summary>
/// Copies the important files without deleting the TweetDeck folder.
/// </summary>
Copy,
/// <summary>
/// Does not copy any files and does not ask the user about data migration again.
/// </summary>

View File

@@ -2,12 +2,13 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Win32;
using TweetDck.Core.Other;
using TweetDck.Migration.Helpers;
using TweetDck.Core.Utils;
namespace TweetDck.Migration{
static class MigrationManager{
@@ -25,7 +26,6 @@ namespace TweetDck.Migration{
switch(decision){
case MigrationDecision.MigratePurge:
case MigrationDecision.Migrate:
case MigrationDecision.Copy:
FormBackgroundWork formWait = new FormBackgroundWork();
formWait.ShowWorkDialog(() => {
@@ -66,7 +66,7 @@ namespace TweetDck.Migration{
}
private static bool BeginMigration(MigrationDecision decision, Action<Exception> onFinished){
if (decision != MigrationDecision.MigratePurge && decision != MigrationDecision.Migrate && decision != MigrationDecision.Copy){
if (decision != MigrationDecision.MigratePurge && decision != MigrationDecision.Migrate){
return false;
}
@@ -83,7 +83,13 @@ namespace TweetDck.Migration{
if (decision == MigrationDecision.Migrate || decision == MigrationDecision.MigratePurge){
// kill process if running
Process runningProcess = ProgramProcessSearch.FindProcessWithWindowByName("TweetDeck");
Process runningProcess = null;
try{
runningProcess = Process.GetProcessesByName("TweetDeck").FirstOrDefault(process => process.MainWindowHandle != IntPtr.Zero);
}catch(Exception){
// process not found
}
if (runningProcess != null){
runningProcess.CloseMainWindow();
@@ -117,7 +123,7 @@ namespace TweetDck.Migration{
if (decision == MigrationDecision.MigratePurge){
// update the lnk files wherever possible (desktop icons, pinned taskbar, start menu)
foreach(string location in GetLnkDirectories()){
if (location == string.Empty)continue;
if (string.IsNullOrEmpty(location))continue;
string linkFile = Path.Combine(location,"TweetDeck.lnk");
@@ -125,7 +131,7 @@ namespace TweetDck.Migration{
LnkEditor lnk = new LnkEditor(linkFile);
lnk.SetPath(Application.ExecutablePath);
lnk.SetWorkingDirectory(Environment.CurrentDirectory);
lnk.SetComment(Program.BrandName); // TODO add a tagline
lnk.SetComment(Program.BrandName+" client for Windows");
lnk.Save();
string renamed = Path.Combine(location,Program.BrandName+".lnk");
@@ -143,7 +149,7 @@ namespace TweetDck.Migration{
}
}
Program.SHChangeNotify(0x8000000,0x1000,IntPtr.Zero,IntPtr.Zero); // refreshes desktop
NativeMethods.SHChangeNotify(0x8000000,0x1000,IntPtr.Zero,IntPtr.Zero); // refreshes desktop
// uninstall in the background
string guid = ProgramRegistrySearch.FindByDisplayName("TweetDeck");
@@ -177,15 +183,6 @@ namespace TweetDck.Migration{
yield return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
yield return Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory);
yield return Environment.ExpandEnvironmentVariables(@"%APPDATA%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar");
/* already handled by the installer
string startMenu = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string[] sub = Directory.GetDirectories(startMenu);
if (sub.Length > 0){
yield return Path.Combine(startMenu,sub[0],"TweetDeck");
}
*/
}
private static void RunUninstaller(string guid, int timeout){

165
Plugins/Controls/PluginControl.Designer.cs generated Normal file
View File

@@ -0,0 +1,165 @@
namespace TweetDck.Plugins.Controls {
partial class PluginControl {
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.btnToggleState = new System.Windows.Forms.Button();
this.labelName = new System.Windows.Forms.Label();
this.panelDescription = new System.Windows.Forms.Panel();
this.labelDescription = new System.Windows.Forms.Label();
this.labelAuthor = new System.Windows.Forms.Label();
this.flowLayoutInfo = new System.Windows.Forms.FlowLayoutPanel();
this.labelWebsite = new System.Windows.Forms.Label();
this.labelVersion = new System.Windows.Forms.Label();
this.panelDescription.SuspendLayout();
this.flowLayoutInfo.SuspendLayout();
this.SuspendLayout();
//
// btnToggleState
//
this.btnToggleState.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnToggleState.Location = new System.Drawing.Point(449, 80);
this.btnToggleState.Name = "btnToggleState";
this.btnToggleState.Size = new System.Drawing.Size(75, 23);
this.btnToggleState.TabIndex = 0;
this.btnToggleState.Text = "Disable";
this.btnToggleState.UseVisualStyleBackColor = true;
this.btnToggleState.Click += new System.EventHandler(this.btnToggleState_Click);
//
// labelName
//
this.labelName.AutoSize = true;
this.labelName.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelName.Location = new System.Drawing.Point(7, 7);
this.labelName.Name = "labelName";
this.labelName.Size = new System.Drawing.Size(61, 24);
this.labelName.TabIndex = 1;
this.labelName.Text = "Name";
//
// panelDescription
//
this.panelDescription.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.panelDescription.AutoScroll = true;
this.panelDescription.Controls.Add(this.labelDescription);
this.panelDescription.Location = new System.Drawing.Point(11, 35);
this.panelDescription.Name = "panelDescription";
this.panelDescription.Size = new System.Drawing.Size(513, 39);
this.panelDescription.TabIndex = 2;
this.panelDescription.Resize += new System.EventHandler(this.panelDescription_Resize);
//
// labelDescription
//
this.labelDescription.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.labelDescription.AutoSize = true;
this.labelDescription.Location = new System.Drawing.Point(0, 0);
this.labelDescription.Margin = new System.Windows.Forms.Padding(0);
this.labelDescription.Name = "labelDescription";
this.labelDescription.Size = new System.Drawing.Size(13, 39);
this.labelDescription.TabIndex = 3;
this.labelDescription.Text = "a\r\nb\r\nc";
//
// labelAuthor
//
this.labelAuthor.AutoSize = true;
this.labelAuthor.Location = new System.Drawing.Point(3, 0);
this.labelAuthor.Margin = new System.Windows.Forms.Padding(3, 0, 32, 0);
this.labelAuthor.Name = "labelAuthor";
this.labelAuthor.Size = new System.Drawing.Size(38, 13);
this.labelAuthor.TabIndex = 3;
this.labelAuthor.Text = "Author";
//
// flowLayoutInfo
//
this.flowLayoutInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.flowLayoutInfo.Controls.Add(this.labelAuthor);
this.flowLayoutInfo.Controls.Add(this.labelWebsite);
this.flowLayoutInfo.Location = new System.Drawing.Point(11, 85);
this.flowLayoutInfo.Name = "flowLayoutInfo";
this.flowLayoutInfo.Size = new System.Drawing.Size(432, 18);
this.flowLayoutInfo.TabIndex = 4;
this.flowLayoutInfo.WrapContents = false;
//
// labelWebsite
//
this.labelWebsite.AutoSize = true;
this.labelWebsite.Cursor = System.Windows.Forms.Cursors.Hand;
this.labelWebsite.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
this.labelWebsite.ForeColor = System.Drawing.Color.Blue;
this.labelWebsite.Location = new System.Drawing.Point(76, 0);
this.labelWebsite.Name = "labelWebsite";
this.labelWebsite.Size = new System.Drawing.Size(46, 13);
this.labelWebsite.TabIndex = 5;
this.labelWebsite.Text = "Website";
this.labelWebsite.Click += new System.EventHandler(this.labelWebsite_Click);
//
// labelVersion
//
this.labelVersion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.labelVersion.Location = new System.Drawing.Point(14, 12);
this.labelVersion.Margin = new System.Windows.Forms.Padding(3, 9, 3, 0);
this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(513, 13);
this.labelVersion.TabIndex = 5;
this.labelVersion.Text = "Version";
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// PluginControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.flowLayoutInfo);
this.Controls.Add(this.panelDescription);
this.Controls.Add(this.labelName);
this.Controls.Add(this.btnToggleState);
this.Controls.Add(this.labelVersion);
this.MaximumSize = new System.Drawing.Size(65535, 109);
this.MinimumSize = new System.Drawing.Size(0, 83);
this.Name = "PluginControl";
this.Padding = new System.Windows.Forms.Padding(3);
this.Size = new System.Drawing.Size(530, 109);
this.panelDescription.ResumeLayout(false);
this.panelDescription.PerformLayout();
this.flowLayoutInfo.ResumeLayout(false);
this.flowLayoutInfo.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnToggleState;
private System.Windows.Forms.Label labelName;
private System.Windows.Forms.Panel panelDescription;
private System.Windows.Forms.Label labelDescription;
private System.Windows.Forms.Label labelAuthor;
private System.Windows.Forms.FlowLayoutPanel flowLayoutInfo;
private System.Windows.Forms.Label labelWebsite;
private System.Windows.Forms.Label labelVersion;
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using TweetDck.Core.Utils;
namespace TweetDck.Plugins.Controls{
partial class PluginControl : UserControl{
private readonly PluginManager pluginManager;
private readonly Plugin plugin;
public PluginControl(){
InitializeComponent();
}
public PluginControl(PluginManager pluginManager, Plugin plugin) : this(){
this.pluginManager = pluginManager;
this.plugin = plugin;
this.labelName.Text = plugin.Name;
this.labelDescription.Text = plugin.CanRun ? plugin.Description : "This plugin requires "+Program.BrandName+" "+plugin.RequiredVersion+" or newer.";
this.labelVersion.Text = plugin.Version;
this.labelAuthor.Text = plugin.Author;
this.labelWebsite.Text = plugin.Website;
this.btnToggleState.Text = pluginManager.Config.IsEnabled(plugin) ? "Disable" : "Enable";
if (!plugin.CanRun){
this.labelName.ForeColor = Color.DarkRed;
this.btnToggleState.Enabled = false;
}
panelDescription_Resize(panelDescription,new EventArgs());
}
private void panelDescription_Resize(object sender, EventArgs e){
labelDescription.MaximumSize = new Size(panelDescription.Width-SystemInformation.VerticalScrollBarWidth,0);
Height = Math.Min(MinimumSize.Height+(labelDescription.Height-13),MaximumSize.Height);
}
private void labelWebsite_Click(object sender, EventArgs e){
if (labelWebsite.Text.Length > 0){
BrowserUtils.OpenExternalBrowser(labelWebsite.Text);
}
}
private void btnToggleState_Click(object sender, EventArgs e){
bool newState = !pluginManager.Config.IsEnabled(plugin);
pluginManager.Config.SetEnabled(plugin,newState);
btnToggleState.Text = newState ? "Disable" : "Enable";
}
}
}

View File

@@ -0,0 +1,3 @@
namespace TweetDck.Plugins.Controls{
partial class PluginListFlowLayout{}
}

View File

@@ -0,0 +1,11 @@
using System.Windows.Forms;
using TweetDck.Core.Utils;
namespace TweetDck.Plugins.Controls{
sealed partial class PluginListFlowLayout : FlowLayoutPanel{
protected override void WndProc(ref Message m){
NativeMethods.ShowScrollBar(Handle,NativeMethods.SB_HORZ,false); // basically fuck the horizontal scrollbar very much
base.WndProc(ref m);
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace TweetDck.Plugins.Events{
class PluginChangedStateEventArgs : EventArgs{
public Plugin Plugin { get; private set; }
public bool IsEnabled { get; private set; }
public PluginChangedStateEventArgs(Plugin plugin, bool isEnabled){
this.Plugin = plugin;
this.IsEnabled = isEnabled;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
namespace TweetDck.Plugins.Events{
class PluginLoadEventArgs : EventArgs{
public bool Success{
get{
return Errors.Count == 0;
}
}
public IList<string> Errors;
public PluginLoadEventArgs(IList<string> errors){
this.Errors = errors;
}
}
}

165
Plugins/Plugin.cs Normal file
View File

@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace TweetDck.Plugins{
class Plugin{
public string Identifier { get { return identifier; } }
public string Name { get { return metadata["NAME"]; } }
public string Description { get { return metadata["DESCRIPTION"]; } }
public string Author { get { return metadata["AUTHOR"]; } }
public string Version { get { return metadata["VERSION"]; } }
public string Website { get { return metadata["WEBSITE"]; } }
public string RequiredVersion { get { return metadata["REQUIRES"]; } }
public PluginGroup Group { get; private set; }
public PluginEnvironment Environments { get; private set; }
public bool CanRun{
get{
return canRun ?? (canRun = CheckRequiredVersion(RequiredVersion)).Value;
}
}
private readonly string path;
private readonly string identifier;
private readonly Dictionary<string,string> metadata = new Dictionary<string,string>(4){
{ "NAME", "" },
{ "DESCRIPTION", "" },
{ "AUTHOR", "(anonymous)" },
{ "VERSION", "(unknown)" },
{ "WEBSITE", "" },
{ "REQUIRES", "*" }
};
private bool? canRun;
private Plugin(string path, PluginGroup group){
this.path = path;
this.identifier = group.GetIdentifierPrefix()+Path.GetFileName(path);
this.Group = group;
this.Environments = PluginEnvironment.None;
}
public string GetScriptPath(PluginEnvironment environment){
if (Environments.HasFlag(environment)){
string file = environment.GetScriptFile();
return file != null ? Path.Combine(path,file) : string.Empty;
}
else{
return string.Empty;
}
}
public override string ToString(){
return Identifier;
}
public override int GetHashCode(){
return identifier.GetHashCode();
}
public override bool Equals(object obj){
Plugin plugin = obj as Plugin;
return plugin != null && plugin.path.Equals(path);
}
public static Plugin CreateFromFolder(string path, PluginGroup group, out string error){
Plugin plugin = new Plugin(path,group);
if (!LoadMetadata(path,plugin,out error)){
return null;
}
if (!LoadEnvironments(path,plugin,out error)){
return null;
}
error = string.Empty;
return plugin;
}
private static bool LoadEnvironments(string path, Plugin plugin, out string error){
foreach(string file in Directory.EnumerateFiles(path,"*.js",SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
PluginEnvironment environment = PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetScriptFile(),StringComparison.Ordinal));
if (environment != PluginEnvironment.None){
plugin.Environments |= environment;
}
else{
error = "Unknown script file: "+file;
return false;
}
}
if (plugin.Environments == PluginEnvironment.None){
error = "Plugin has no script files.";
return false;
}
error = string.Empty;
return true;
}
private static readonly string[] endTag = { "[END]" };
private static bool LoadMetadata(string path, Plugin plugin, out string error){
string metaFile = Path.Combine(path,".meta");
if (!File.Exists(metaFile)){
error = "Missing .meta file.";
return false;
}
string[] lines = File.ReadAllLines(metaFile,Encoding.UTF8);
string currentTag = null, currentContents = "";
foreach(string line in lines.Concat(endTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
if (line[0] == '[' && line[line.Length-1] == ']'){
if (currentTag != null){
plugin.metadata[currentTag] = currentContents;
}
currentTag = line.Substring(1,line.Length-2).ToUpperInvariant();
currentContents = "";
if (line.Equals(endTag[0])){
break;
}
if (!plugin.metadata.ContainsKey(currentTag)){
error = "Invalid metadata tag: "+currentTag;
return false;
}
}
else if (currentTag != null){
currentContents = currentContents.Length == 0 ? line : currentContents+"\r\n"+line;
}
else{
error = "Missing metadata tag before value: "+line;
return false;
}
}
if (plugin.Name.Length == 0){
error = "Plugin is missing a name in the .meta file.";
return false;
}
Version ver;
if (plugin.RequiredVersion.Length == 0 || !(plugin.RequiredVersion.Equals("*") || System.Version.TryParse(plugin.RequiredVersion,out ver))){
error = "Plugin contains invalid version: "+plugin.RequiredVersion;
return false;
}
error = string.Empty;
return true;
}
private static bool CheckRequiredVersion(string requires){
return requires.Equals("*",StringComparison.Ordinal) || Program.Version >= new Version(requires);
}
}
}

37
Plugins/PluginConfig.cs Normal file
View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using TweetDck.Plugins.Events;
namespace TweetDck.Plugins{
[Serializable]
class PluginConfig{
[field:NonSerialized]
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
public IEnumerable<string> DisabledPlugins{
get{
return Disabled;
}
}
public bool AnyDisabled{
get{
return Disabled.Count > 0;
}
}
private readonly HashSet<string> Disabled = new HashSet<string>();
public void SetEnabled(Plugin plugin, bool enabled){
if ((enabled && Disabled.Remove(plugin.Identifier)) || (!enabled && Disabled.Add(plugin.Identifier))){
if (PluginChangedState != null){
PluginChangedState(this,new PluginChangedStateEventArgs(plugin,enabled));
}
}
}
public bool IsEnabled(Plugin plugin){
return !Disabled.Contains(plugin.Identifier) && plugin.CanRun;
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace TweetDck.Plugins{
[Flags]
enum PluginEnvironment{
None = 0,
Browser = 1,
Notification = 2
}
static class PluginEnvironmentExtensions{
public static IEnumerable<PluginEnvironment> Values{
get{
yield return PluginEnvironment.Browser;
yield return PluginEnvironment.Notification;
}
}
public static string GetScriptFile(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.Browser: return "browser.js";
case PluginEnvironment.Notification: return "notification.js";
default: return null;
}
}
public static string GetScriptVariables(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.Browser: return "$,$TD,TD";
case PluginEnvironment.Notification: return "$TD";
default: return string.Empty;
}
}
}
}

15
Plugins/PluginGroup.cs Normal file
View File

@@ -0,0 +1,15 @@
namespace TweetDck.Plugins{
enum PluginGroup{
Official, Custom
}
static class PluginGroupExtensions{
public static string GetIdentifierPrefix(this PluginGroup group){
switch(group){
case PluginGroup.Official: return "official/";
case PluginGroup.Custom: return "custom/";
default: return "unknown/";
}
}
}
}

99
Plugins/PluginManager.cs Normal file
View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CefSharp;
using TweetDck.Plugins.Events;
using TweetDck.Resources;
namespace TweetDck.Plugins{
class PluginManager{
public const string PluginBrowserScriptFile = "plugins.browser.js";
public const string PluginNotificationScriptFile = "plugins.notification.js";
public string PathOfficialPlugins { get { return Path.Combine(rootPath,"official"); } }
public string PathCustomPlugins { get { return Path.Combine(rootPath,"user"); } }
public IEnumerable<Plugin> Plugins { get { return plugins; } }
public PluginConfig Config { get; private set; }
public event EventHandler<PluginLoadEventArgs> Reloaded;
private readonly string rootPath;
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
private List<string> loadErrors;
public PluginManager(string path, PluginConfig config){
this.rootPath = path;
this.Config = config;
}
public IEnumerable<Plugin> GetPluginsByGroup(PluginGroup group){
return plugins.Where(plugin => plugin.Group == group);
}
public int CountPluginByGroup(PluginGroup group){
return plugins.Count(plugin => plugin.Group == group);
}
public bool HasAnyPlugin(PluginEnvironment environment){
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
}
public void Reload(){
HashSet<Plugin> prevPlugins = new HashSet<Plugin>(plugins);
plugins.Clear();
loadErrors = new List<string>(2);
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins,PluginGroup.Official)){
plugins.Add(plugin);
}
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins,PluginGroup.Custom)){
plugins.Add(plugin);
}
if (Reloaded != null && (loadErrors.Count > 0 || !prevPlugins.SetEquals(plugins))){
Reloaded(this,new PluginLoadEventArgs(loadErrors));
}
}
public void ExecutePlugins(IFrame frame, PluginEnvironment environment, bool includeDisabled){
if (includeDisabled){
ScriptLoader.ExecuteScript(frame,PluginScriptGenerator.GenerateConfig(Config),"gen:pluginconfig");
}
foreach(Plugin plugin in Plugins){
string path = plugin.GetScriptPath(environment);
if (string.IsNullOrEmpty(path) || !plugin.CanRun || (!includeDisabled && !Config.IsEnabled(plugin)))continue;
string script;
try{
script = File.ReadAllText(path);
}catch{
// TODO
continue;
}
ScriptLoader.ExecuteScript(frame,PluginScriptGenerator.GeneratePlugin(plugin.Identifier,script,environment),"plugin:"+plugin);
}
}
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
foreach(string fullDir in Directory.EnumerateDirectories(path,"*",SearchOption.TopDirectoryOnly)){
string error;
Plugin plugin = Plugin.CreateFromFolder(fullDir,group,out error);
if (plugin == null){
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
}
else{
yield return plugin;
}
}
}
}
}

View File

@@ -0,0 +1,24 @@
using System.Text;
namespace TweetDck.Plugins{
static class PluginScriptGenerator{
public static string GenerateConfig(PluginConfig config){
return config.AnyDisabled ? "window.TD_PLUGINS.disabled = [\""+string.Join("\",\"",config.DisabledPlugins)+"\"];" : string.Empty;
}
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, PluginEnvironment environment){
StringBuilder build = new StringBuilder(pluginIdentifier.Length+pluginContents.Length+110);
build.Append("(function(").Append(environment.GetScriptVariables()).Append("){");
build.Append("window.TD_PLUGINS.install({");
build.Append("id:\"").Append(pluginIdentifier).Append("\",");
build.Append("obj:new class extends PluginBase{").Append(pluginContents).Append("}");
build.Append("});");
build.Append("})(").Append(environment.GetScriptVariables()).Append(");");
return build.ToString();
}
}
}

View File

@@ -2,7 +2,6 @@
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using CefSharp;
@@ -10,6 +9,11 @@ using TweetDck.Configuration;
using TweetDck.Core;
using TweetDck.Migration;
using TweetDck.Core.Utils;
using System.Linq;
using System.Threading;
using TweetDck.Plugins;
using TweetDck.Plugins.Events;
using TweetDck.Core.Other.Settings.Export;
[assembly: CLSCompliant(true)]
namespace TweetDck{
@@ -22,13 +26,22 @@ namespace TweetDck{
public const string Website = "http://tweetdick.chylex.com";
#endif
public const string VersionTag = "1.2.1";
public const string VersionFull = "1.2.1.0";
public const string BrowserSubprocess = BrandName+".Browser.exe";
public const string VersionTag = "1.3";
public const string VersionFull = "1.3.0.0";
public static readonly Version Version = new Version(VersionTag);
public static readonly string StoragePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),BrandName);
public static readonly string PluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"plugins");
public static readonly string TemporaryPath = Path.Combine(Path.GetTempPath(),BrandName);
public static readonly string ConfigFilePath = Path.Combine(StoragePath,"TD_UserConfig.cfg");
public static uint WindowRestoreMessage;
private static readonly LockManager LockManager = new LockManager(Path.Combine(StoragePath,".lock"));
private static bool HasCleanedUp;
public static UserConfig UserConfig { get; private set; }
@@ -38,25 +51,36 @@ namespace TweetDck{
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr LoadLibrary(string name);
[DllImport("Shell32.dll")]
public static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern bool SetWindowPos(int hWnd, int hWndOrder, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
[STAThread]
private static void Main(){
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");
string[] programArguments = Environment.GetCommandLineArgs();
if (programArguments.Contains("-restart")){
for(int attempt = 0; attempt < 21; attempt++){
if (LockManager.Lock()){
break;
}
else if (attempt == 20){
MessageBox.Show(BrandName+" is taking too long to close, please wait and then start the application again manually.",BrandName+" Cannot Restart",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
else{
Thread.Sleep(500);
}
}
}
else{
if (!LockManager.Lock()){
if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?",BrandName+" is Already Running",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
if (LockManager.LockingProcess.MainWindowHandle == IntPtr.Zero && LockManager.LockingProcess.Responding){ // restore if the original process is in tray
NativeMethods.SendMessage(NativeMethods.HWND_BROADCAST,WindowRestoreMessage,0,IntPtr.Zero);
return;
}
else if (MessageBox.Show("Another instance of "+BrandName+" is already running.\r\nDo you want to close it?",BrandName+" is Already Running",MessageBoxButtons.YesNo,MessageBoxIcon.Error,MessageBoxDefaultButton.Button2) == DialogResult.Yes){
if (!LockManager.CloseLockingProcess(10000)){
MessageBox.Show("Could not close the other process.",BrandName+" Has Failed :(",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
@@ -66,8 +90,21 @@ namespace TweetDck{
}
else return;
}
}
UserConfig = UserConfig.Load(Path.Combine(StoragePath,"TD_UserConfig.cfg"));
if (programArguments.Contains("-importcookies") && File.Exists(ExportManager.TempCookiesPath)){
try{
if (File.Exists(ExportManager.CookiesPath)){
File.Delete(ExportManager.CookiesPath);
}
File.Move(ExportManager.TempCookiesPath,ExportManager.CookiesPath);
}catch(Exception e){
HandleException("Could not import the cookie file to restore login session.",e);
}
}
ReloadConfig();
MigrationManager.Run();
@@ -83,8 +120,9 @@ namespace TweetDck{
UserAgent = BrowserUtils.HeaderUserAgent,
Locale = CultureInfo.CurrentCulture.TwoLetterISOLanguageName,
CachePath = StoragePath,
BrowserSubprocessPath = File.Exists(BrowserSubprocess) ? BrowserSubprocess : "CefSharp.BrowserSubprocess.exe",
#if !DEBUG
LogSeverity = LogSeverity.Disable
LogSeverity = programArguments.Contains("-log") ? LogSeverity.Info : LogSeverity.Disable
#endif
});
@@ -96,13 +134,14 @@ namespace TweetDck{
}
};
Application.ApplicationExit += (sender, args) => {
UserConfig.Save();
ExitCleanup();
Cef.Shutdown();
};
Application.ApplicationExit += (sender, args) => ExitCleanup();
FormBrowser mainForm = new FormBrowser();
PluginManager plugins = new PluginManager(PluginPath,UserConfig.Plugins);
plugins.Reloaded += plugins_Reloaded;
plugins.Config.PluginChangedState += (sender, args) => UserConfig.Save();
plugins.Reload();
FormBrowser mainForm = new FormBrowser(plugins);
Application.Run(mainForm);
if (mainForm.UpdateInstallerPath != null){
@@ -113,6 +152,12 @@ namespace TweetDck{
}
}
private static void plugins_Reloaded(object sender, PluginLoadEventArgs e){
if (!e.Success){
MessageBox.Show("The following plugins will not be available until the issues are resolved:\n"+string.Join("\n",e.Errors),"Error Loading Plugins",MessageBoxButtons.OK,MessageBoxIcon.Warning);
}
}
public static void HandleException(string message, Exception e){
Log(e.ToString());
@@ -138,9 +183,28 @@ namespace TweetDck{
}
}
private static void ExitCleanup(){
public static void ReloadConfig(){
UserConfig = UserConfig.Load(ConfigFilePath);
}
public static void ResetConfig(){
try{
File.Delete(ConfigFilePath);
File.Delete(UserConfig.GetBackupFile(ConfigFilePath));
}catch(Exception e){
HandleException("Could not delete configuration files to reset the settings.",e);
return;
}
ReloadConfig();
}
private static void ExitCleanup(){
if (HasCleanedUp)return;
UserConfig.Save();
try{
LockManager.Unlock();
Directory.Delete(TemporaryPath,true);
}catch(DirectoryNotFoundException){
}catch(Exception e){
@@ -149,6 +213,10 @@ namespace TweetDck{
}
Cef.Shutdown();
BrowserCache.Exit();
LockManager.Unlock();
HasCleanedUp = true;
}
}
}

View File

@@ -69,5 +69,25 @@ namespace TweetDck.Properties {
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon icon_tray {
get {
object obj = ResourceManager.GetObject("icon_tray", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon icon_tray_new {
get {
object obj = ResourceManager.GetObject("icon_tray_new", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View File

@@ -121,4 +121,10 @@
<data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="icon_tray" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon-tray.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="icon_tray_new" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\icon-tray-new.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@@ -0,0 +1,15 @@
[name]
Revert TweetDeck design changes
[description]
- Moves action menu to the right and hides it by default
- Reverts interactive texts around tweets (such as 'Details' or 'Conversation')
[author]
chylex
[version]
1.0
[website]
http://tweetduck.chylex.com

View File

@@ -0,0 +1,35 @@
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; visibility: hidden; }",0);
sheet.insertRule(".tweet-actions:hover { visibility: visible; }",0);
sheet.insertRule(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }",0);
// revert small links around the tweet
this.prevFooterMustache = TD.mustaches["status/tweet_single_footer.mustache"];
var footerLayout = TD.mustaches["status/tweet_single_footer.mustache"];
footerLayout = footerLayout.replace('txt-mute txt-size--12','txt-mute txt-small');
footerLayout = footerLayout.replace('{{#inReplyToID}}','{{^inReplyToID}} <a class="pull-left margin-txs txt-mute txt-small is-vishidden-narrow" href="#" rel="viewDetails">{{_i}}Details{{/i}}</a> <a class="pull-left margin-txs txt-mute txt-small is-vishidden is-visshown-narrow" href="#" rel="viewDetails">{{_i}}Open{{/i}}</a> {{/inReplyToID}} {{#inReplyToID}}');
footerLayout = footerLayout.replace('<span class="link-complex-target"> {{_i}}View Conversation{{/i}}','<i class="icon icon-conversation icon-small-context"></i> <span class="link-complex-target"> <span class="is-vishidden-wide is-vishidden-narrow">{{_i}}View{{/i}}</span> <span class="is-vishidden is-visshown-wide">{{_i}}Conversation{{/i}}</span>');
TD.mustaches["status/tweet_single_footer.mustache"] = footerLayout;
// fix layout for right-aligned actions menu
this.uiShowActionsMenuEvent = function(){
$(".js-dropdown.pos-r").toggleClass("pos-r pos-l");
};
}
ready(){
$(document).on("uiShowActionsMenu",this.uiShowActionsMenuEvent);
}
disabled(){
$("#design-revert").remove();
$(document).off("uiShowActionsMenu",this.uiShowActionsMenuEvent);
TD.mustaches["status/tweet_single_footer.mustache"] = this.prevFooterMustache;
}

View File

@@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using CefSharp;
using CefSharp.WinForms;
namespace TweetDck.Resources{
static class ScriptLoader{
private const string UrlPrefix = "td:";
public static string LoadResource(string name){
try{
return File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,name),Encoding.UTF8);
@@ -16,8 +18,30 @@ namespace TweetDck.Resources{
}
}
public static IList<string> LoadResources(params string[] names){
return names.Select(LoadResource).ToList();
public static void ExecuteFile(ChromiumWebBrowser browser, string file){
ExecuteScript(browser,LoadResource(file),GetRootIdentifier(file));
}
public static void ExecuteFile(IFrame frame, string file){
ExecuteScript(frame,LoadResource(file),GetRootIdentifier(file));
}
public static void ExecuteScript(ChromiumWebBrowser browser, string script, string identifier){
if (script == null)return;
using(IFrame frame = browser.GetMainFrame()){
frame.ExecuteJavaScriptAsync(script,UrlPrefix+identifier,1);
}
}
public static void ExecuteScript(IFrame frame, string script, string identifier){
if (script == null)return;
frame.ExecuteJavaScriptAsync(script,UrlPrefix+identifier,1);
}
public static string GetRootIdentifier(string file){
return "root:"+Path.GetFileNameWithoutExtension(file);
}
}
}

View File

@@ -1,8 +1,13 @@
(function($,$TD,TD){
//
// Variable: Says whether TweetD*ck events was initialized.
// Variable: Current highlighted column jQuery object.
//
var isInitialized = false;
var highlightedColumnEle;
//
// Variable: Currently highlighted tweet jQuery object.
//
var highlightedTweetEle;
//
// Function: Initializes TweetD*ck events. Called after the website app is loaded.
@@ -14,15 +19,26 @@
var menu = $(".js-dropdown-content").children("ul").first();
if (menu.length === 0)return;
menu.children(".drp-h-divider").last().after('<li class="is-selectable" data-std><a href="#" data-action>'+$TD.brandName+' settings</a></li><li class="drp-h-divider"></li>');
menu.children(".drp-h-divider").last().after([
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">'+$TD.brandName+' settings</a></li>',
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">'+$TD.brandName+' plugins</a></li>',
'<li class="drp-h-divider"></li>'
].join(""));
var tweetDckBtn = menu.children("[data-std]").first();
var buttons = menu.children("[data-std]");
tweetDckBtn.on("click","a",function(){
buttons.on("click","a",function(){
var action = $(this).attr("data-action");
if (action === "td-settings"){
$TD.openSettingsMenu();
}
else if (action === "td-plugins"){
$TD.openPluginsMenu();
}
});
tweetDckBtn.hover(function(){
buttons.hover(function(){
$(this).addClass("is-selected");
},function(){
$(this).removeClass("is-selected");
@@ -32,16 +48,30 @@
// Notification handling
$.subscribe("/notifications/new",function(obj){
for(var item of obj.items){
onNewTweet(obj.column,item);
for(let index = obj.items.length-1; index >= 0; index--){
onNewTweet(obj.column,obj.items[index]);
}
});
// Finish init
// Setup video element replacement
new MutationObserver(function(){
$("video").each(function(){
$(this).parent().replaceWith("<a href='"+$(this).attr("src")+"' rel='url' target='_blank' style='display:block; border:1px solid #555; padding:3px 6px'>&#9658; Open video in browser</a>");
});
}).observe($(".js-app-columns")[0],{
childList: true,
subtree: true
});
// Finish init and load plugins
$TD.loadFontSizeClass(TD.settings.getFontSize());
$TD.loadNotificationHeadContents(getNotificationHeadContents());
isInitialized = true;
window.TD_APP_READY = true;
if (window.TD_PLUGINS){
window.TD_PLUGINS.onReady();
}
};
//
@@ -76,18 +106,11 @@
}));
html.css("border","0");
html.find(".tweet-body").first().children("footer").remove();
var body = html.find(".tweet-body").first();
var url = html.find("time").first().children("a").first().attr("href") || "";
body.children("div.js-quote-detail").each(function(){
$(this).html("(quoted tweet)");
$(this).removeClass("padding-al");
$(this).css("padding","6px");
});
body.children("footer").remove();
$TD.onTweetPopup(html.html(),tweet.text.length); // TODO column
$TD.onTweetPopup(html.html(),url,tweet.text.length); // TODO column
}
else if (column.model.getHasSound()){
$TD.onTweetSound(); // TODO disable original
@@ -112,11 +135,11 @@
//
var app = $("body").children(".js-app");
new MutationObserver(function(mutations){
if (isInitialized && app.hasClass("is-hidden")){
isInitialized = false;
new MutationObserver(function(){
if (window.TD_APP_READY && app.hasClass("is-hidden")){
window.TD_APP_READY = false;
}
else if (!isInitialized && !app.hasClass("is-hidden")){
else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
initializeTweetDck();
}
}).observe(app[0],{
@@ -138,7 +161,7 @@
});
//
// Block: Force popup notification settings
// Block: Force popup notification settings.
//
TD.controller.notifications.hasNotifications = function(){
return true;
@@ -149,63 +172,17 @@
};
//
// Block: Hook into links to bypass default open function
//
(function(){
var urlWait = false;
var onUrlOpened = function(){
urlWait = true;
setTimeout(function(){ urlWait = false; },0);
};
$(document.body).delegate("a[target='_blank']","click",function(e){
if (urlWait)return;
var me = $(this);
var rel = me.attr("rel");
if (!me.is(".link-complex") && !(rel === "mediaPreview" && me.closest("#open-modal").length === 0) && rel !== "list" && rel !== "user"){
$TD.openBrowser(me.attr("href"));
onUrlOpened();
}
e.preventDefault();
});
window.open = function(url){
if (urlWait)return;
$TD.openBrowser(url);
onUrlOpened();
};
TD.util.maybeOpenClickExternally = prependToFunction(TD.util.maybeOpenClickExternally,function(e){
if (e.ctrlKey){
if (urlWait)return;
$TD.openBrowser(e.currentTarget.getAttribute("href"));
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return true;
}
});
})();
//
// Block: Expand shortened links on hover.
// Block: Expand shortened links on hover or display tooltip.
//
(function(){
var cutStart = function(str, search){
return _.startsWith(str,search) ? str.substr(search.length) : str;
return str.startsWith(search) ? str.substr(search.length) : str;
};
$(document.body).delegate("a[data-full-url]","mouseenter mouseleave",function(e){
if (!$TD.expandLinksOnHover){
return;
}
var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed;
$(document.body).delegate("a[data-full-url]","mouseenter mouseleave mousemove",function(e){
var me = $(this);
if (e.type === "mouseenter"){
@@ -215,6 +192,8 @@
return;
}
if ($TD.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = me.attr("data-full-url");
expanded = cutStart(expanded,"https://");
expanded = cutStart(expanded,"http://");
@@ -222,14 +201,38 @@
me.attr("td-prev-text",text);
me.text(expanded);
},200);
}
else{
tooltipTimer = window.setTimeout(function(){
$TD.displayTooltip(me.attr("data-full-url"),false);
tooltipDisplayed = true;
},400);
}
}
else if (e.type === "mouseleave"){
if ($TD.expandLinksOnHover){
var prevText = me.attr("td-prev-text");
if (prevText){
me.text(prevText);
}
}
window.clearTimeout(tooltipTimer);
if (tooltipDisplayed){
tooltipDisplayed = false;
$TD.displayTooltip(null,false);
}
}
else if (e.type === "mousemove"){
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
$TD.displayTooltip(me.attr("data-full-url"),false);
prevMouseX = e.clientX;
prevMouseY = e.clientY;
}
}
});
})();
@@ -256,17 +259,46 @@
};*/
//
// Block: Hook into mp4 video element clicking
// Block: Update highlighted column.
//
$(document.body).delegate("video.js-media-gif","click",function(e){
var src = $(this).attr("src");
if (src.endsWith(".mp4")){
$TD.openBrowser(src);
e.preventDefault();
app.delegate("section","mouseenter mouseleave",function(e){
if (e.type === "mouseenter"){
highlightedColumnEle = $(this);
}
else if (e.type === "mouseleave"){
highlightedColumnEle = null;
}
});
//
// Block: Copy tweet address and update highlighted tweet.
//
(function(){
var lastTweet = "";
var updateHighlightedTweet = function(link, embeddedLink){
if (lastTweet !== link){
$TD.setLastHighlightedTweet(link,embeddedLink);
lastTweet = link;
}
};
app.delegate("article.js-stream-item","mouseenter mouseleave",function(e){
if (e.type === "mouseenter"){
highlightedTweetEle = $(this);
var link = $(this).find("time").first().children("a").first();
var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();
updateHighlightedTweet(link.length > 0 ? link.attr("href") : "",embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
}
else if (e.type === "mouseleave"){
highlightedTweetEle = null;
updateHighlightedTweet("","");
}
});
})();
//
// Block: Paste images when tweeting.
//
@@ -291,7 +323,7 @@
$TD.clickUploadImage(Math.floor(buttonPos.left),Math.floor(buttonPos.top));
};
$(".js-app").delegate(".js-compose-text","paste",function(e){
$(".js-app").delegate(".js-compose-text,.js-reply-tweetbox","paste",function(){
lastPasteElement = $(this);
$TD.tryPasteImage();
});
@@ -301,7 +333,7 @@
var parent = lastPasteElement.parent();
if (parent.siblings(".js-add-image-button").length === 0){
var pop = parent.closest(".js-inline-reply").find(".js-inline-compose-pop");
var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");
if (pop.length === 0){
lastPasteElement = null;
@@ -340,25 +372,38 @@
})();
//
// Block: Inject custom CSS and layout into the page
// Block: Support for extra mouse buttons.
//
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{
$(".js-column-back").click();
}
}
else if (button === 2){ // forward button
if (highlightedTweetEle){
highlightedTweetEle.children().first().click();
}
}
};
//
// Block: Inject custom CSS and layout into the page.
//
(function(){
var style = document.createElement("style");
document.head.appendChild(style);
var sheet = style.sheet;
// change View Conversation
var footerLayout = TD.mustaches["status/tweet_single_footer.mustache"];
footerLayout = footerLayout.replace('txt-mute txt-size--12','txt-mute txt-small');
footerLayout = footerLayout.replace('{{#inReplyToID}}','{{^inReplyToID}} <a class="pull-left margin-txs txt-mute txt-small is-vishidden-narrow" href="#" rel="viewDetails">{{_i}}Details{{/i}}</a> <a class="pull-left margin-txs txt-mute txt-small is-vishidden is-visshown-narrow" href="#" rel="viewDetails">{{_i}}Open{{/i}}</a> {{/inReplyToID}} {{#inReplyToID}}');
footerLayout = footerLayout.replace('<span class="link-complex-target"> {{_i}}View Conversation{{/i}}','<i class="icon icon-conversation icon-small-context"></i> <span class="link-complex-target"> <span class="is-vishidden-wide is-vishidden-narrow">{{_i}}View{{/i}}</span> <span class="is-vishidden is-visshown-wide">{{_i}}Conversation{{/i}}</span>');
TD.mustaches["status/tweet_single_footer.mustache"] = footerLayout;
// tweet actions
sheet.insertRule(".tweet-actions { float: right !important; width: auto !important; visibility: hidden; }",0);
sheet.insertRule(".tweet-actions:hover { visibility: visible; }",0);
sheet.insertRule(".tweet-actions > li:nth-child(4) { margin-right: 2px !important; }",0);
// break long urls
sheet.insertRule("a[data-full-url] { word-break: break-all; }",0);
})();

View File

@@ -0,0 +1,124 @@
(function($TD){
//
// Variable: Collection of all <a> tags.
//
var links = document.getElementsByTagName("A");
//
// Function: Adds an event listener to all elements in the array or collection.
//
var addEventListener = function(collection, type, listener){
for(let index = 0; index < collection.length; index++){
collection[index].addEventListener(type,listener);
}
};
//
// Block: Hook into links to bypass default open function.
//
addEventListener(links,"click",function(e){
$TD.openBrowser(e.currentTarget.getAttribute("href"));
e.preventDefault();
});
//
// Block: Allow bypassing of t.co in context menus.
//
addEventListener(links,"contextmenu",function(e){
$TD.setLastRightClickedLink(e.currentTarget.getAttribute("data-full-url") || "");
});
//
// Block: Expand shortened links on hover or display tooltip.
//
(function(){
var cutStart = function(str, search){
return str.startsWith(search) ? str.substr(search.length) : str;
};
var prevMouseX = -1, prevMouseY = -1;
var tooltipTimer, tooltipDisplayed;
addEventListener(links,"mouseenter",function(e){
var me = e.currentTarget;
var url = me.getAttribute("data-full-url");
if (!url)return;
var text = me.textContent;
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
return;
}
if ($TD.expandLinksOnHover){
tooltipTimer = window.setTimeout(function(){
var expanded = url;
expanded = cutStart(expanded,"https://");
expanded = cutStart(expanded,"http://");
expanded = cutStart(expanded,"www.");
me.setAttribute("td-prev-text",text);
me.innerHTML = expanded;
},200);
}
else{
tooltipTimer = window.setTimeout(function(){
$TD.displayTooltip(url,true);
tooltipDisplayed = true;
},400);
}
});
addEventListener(links,"mouseleave",function(e){
if (!e.currentTarget.hasAttribute("data-full-url"))return;
if ($TD.expandLinksOnHover){
var prevText = e.currentTarget.getAttribute("td-prev-text");
if (prevText){
e.currentTarget.innerHTML = prevText;
}
}
window.clearTimeout(tooltipTimer);
if (tooltipDisplayed){
tooltipDisplayed = false;
$TD.displayTooltip(null,true);
}
});
addEventListener(links,"mousemove",function(e){
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
var url = e.currentTarget.getAttribute("data-full-url");
if (!url)return;
$TD.displayTooltip(url,true);
prevMouseX = e.clientX;
prevMouseY = e.clientY;
}
});
})();
//
// Block: Setup embedded tweet address for context menu.
//
(function(){
var embedded = document.getElementsByClassName("quoted-tweet");
if (embedded.length === 0)return;
var tweetId = embedded[0].getAttribute("data-tweet-id");
if (!tweetId)return;
var account = embedded[0].getElementsByClassName("account-link");
if (account.length === 0)return;
$TD.setNotificationTweetEmbedded(account[0].getAttribute("href")+"/status/"+tweetId);
})();
//
// Block: Page fully loaded.
//
$TD.onNotificationReady();
})($TD);

View File

@@ -0,0 +1,80 @@
(function(){
//
// Class: Abstract plugin base class.
//
window.PluginBase = class{
constructor(pluginSettings){
this.$pluginSettings = pluginSettings || {};
}
enabled(){}
ready(){}
disabled(){}
};
//
// Variable: Main object for containing and managing plugins.
//
window.TD_PLUGINS = new class{
constructor(){
this.installed = [];
this.disabled = [];
this.waiting = [];
}
isDisabled(plugin){
return this.disabled.includes(plugin.id);
}
findObject(identifier){
return this.installed.find(plugin => plugin.id === identifier);
}
install(plugin){
this.installed.push(plugin);
if (!this.isDisabled(plugin)){
plugin.obj.enabled();
this.runWhenReady(plugin);
}
}
runWhenReady(plugin){
if (window.TD_APP_READY){
plugin.obj.ready();
}
else{
this.waiting.push(plugin);
}
}
setState(plugin, enable){
if (enable && this.isDisabled(plugin)){
this.disabled.splice(this.disabled.indexOf(plugin.id),1);
plugin.obj.enabled();
this.runWhenReady(plugin);
}
else if (!enable && !this.isDisabled(plugin)){
this.disabled.push(plugin.id);
plugin.obj.disabled();
}
else return;
if (plugin.obj.$pluginSettings.requiresPageReload){
window.location.reload();
}
}
onReady(){
this.waiting.forEach(plugin => plugin.obj.ready());
this.waiting = [];
}
};
//
// Block: Setup global function to change plugin state.
//
window.TDPF_setPluginState = function(identifier, enable){
window.TD_PLUGINS.setState(window.TD_PLUGINS.findObject(identifier),enable);
};
})();

View File

@@ -0,0 +1,21 @@
(function(){
//
// Class: Abstract plugin base class.
//
window.PluginBase = class{
constructor(pluginSettings){
this.$pluginSettings = pluginSettings || {};
}
run(){}
};
//
// Variable: Main object for containing and managing plugins.
//
window.TD_PLUGINS = {
install: function(plugin){
plugin.obj.run();
}
};
})();

View File

@@ -1,9 +1,19 @@
(function($,$TD){
(function($,$TDU){
//
// Variable: Current timeout ID for update checking.
//
var updateCheckTimeoutID;
//
// Constant: Update exe file name.
//
const updateFileName = $TDU.brandName+".Update.exe";
//
// Constant: Url that returns JSON data about latest version.
//
const updateCheckUrl = "https://api.github.com/repos/chylex/"+$TDU.brandName+"/releases/latest";
//
// Function: Creates the update notification element. Removes the old one if already exists.
//
@@ -17,7 +27,7 @@
var html = [
"<div id='tweetdck-update'>",
"<p class='tdu-title'>"+$TD.brandName+" Update</p>",
"<p class='tdu-title'>"+$TDU.brandName+" Update</p>",
"<p class='tdu-info'>Version "+version+" is now available.</p>",
"<div class='tdu-buttons'>",
"<button class='btn btn-positive tdu-btn-download'><span class='label'>Download</button>",
@@ -80,11 +90,11 @@
buttonDiv.children(".tdu-btn-download").click(function(){
ele.remove();
$TD.onUpdateAccepted(version,download);
$TDU.onUpdateAccepted(version,download);
});
buttonDiv.children(".tdu-btn-dismiss").click(function(){
$TD.onUpdateDismissed(version);
$TDU.onUpdateDismissed(version);
ele.slideUp(function(){ ele.remove(); });
});
@@ -96,19 +106,25 @@
};
//
// Function: Runs an update check and updates all DOM elements appropriately
// Function: Runs an update check and updates all DOM elements appropriately.
//
var runUpdateCheck = function(){
var runUpdateCheck = function(force, eventID){
clearTimeout(updateCheckTimeoutID);
updateCheckTimeoutID = setTimeout(runUpdateCheck,1000*60*60); // 1 hour
if (!$TD.updateCheckEnabled)return;
if (!$TDU.updateCheckEnabled && !force)return;
$.getJSON("https://api.github.com/repos/chylex/"+$TD.brandName+"/releases/latest",function(response){
$.getJSON(updateCheckUrl,function(response){
var tagName = response.tag_name;
var hasUpdate = tagName !== $TDU.versionTag && tagName !== $TDU.dismissedVersionTag && response.assets.length > 0;
if (tagName != $TD.versionTag && tagName != $TD.dismissedVersionTag && response.assets.length > 0){
createUpdateNotificationElement(tagName,response.assets[0].browser_download_url);
if (hasUpdate){
var obj = response.assets.find(asset => asset.name === updateFileName) || response.assets[0];
createUpdateNotificationElement(tagName,obj.browser_download_url);
}
if (eventID !== 0){
$TDU.onUpdateCheckFinished(eventID,hasUpdate,tagName);
}
});
};
@@ -116,6 +132,6 @@
//
// Block: Setup global functions.
//
window.TDGF_runUpdateCheck = runUpdateCheck;
window.TDUF_runUpdateCheck = runUpdateCheck;
runUpdateCheck();
})($,$TD);
})($,$TDU);

BIN
Resources/about.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
Resources/icon-tray-new.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
Resources/icon-tray.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

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

View File

@@ -84,6 +84,18 @@
<Compile Include="Core\Controls\FlatProgressBar.Designer.cs">
<DependentUpon>FlatProgressBar.cs</DependentUpon>
</Compile>
<Compile Include="Core\Controls\TabButton.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Core\Controls\TabButton.Designer.cs">
<DependentUpon>TabButton.cs</DependentUpon>
</Compile>
<Compile Include="Core\Controls\TabPanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Controls\TabPanel.Designer.cs">
<DependentUpon>TabPanel.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\ContextMenuBase.cs" />
<Compile Include="Core\Handling\ContextMenuBrowser.cs" />
<Compile Include="Core\FormBrowser.cs">
@@ -100,23 +112,57 @@
</Compile>
<Compile Include="Core\Handling\ContextMenuNotification.cs" />
<Compile Include="Core\Handling\DialogHandlerBrowser.cs" />
<Compile Include="Core\Handling\LifeSpanHandler.cs" />
<Compile Include="Core\Handling\TweetNotification.cs" />
<Compile Include="Core\Controls\RichTextLabel.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Core\Controls\RichTextLabel.Designer.cs">
<DependentUpon>RichTextLabel.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormAbout.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormAbout.Designer.cs">
<DependentUpon>FormAbout.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormBackgroundWork.cs">
<Compile Include="Core\Other\FormPlugins.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormBackgroundWork.Designer.cs">
<Compile Include="Core\Other\FormPlugins.Designer.cs">
<DependentUpon>FormPlugins.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\Export\CombinedFileStream.cs" />
<Compile Include="Core\Other\Settings\Export\ExportManager.cs" />
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsAdvanced.Designer.cs">
<DependentUpon>TabSettingsAdvanced.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\BaseTabSettings.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\BaseTabSettings.Designer.cs">
<DependentUpon>BaseTabSettings.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsGeneral.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsGeneral.Designer.cs">
<DependentUpon>TabSettingsGeneral.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsNotifications.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsNotifications.Designer.cs">
<DependentUpon>TabSettingsNotifications.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsUpdates.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Core\Other\Settings\TabSettingsUpdates.Designer.cs">
<DependentUpon>TabSettingsUpdates.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\WindowState.cs" />
<Compile Include="Migration\FormBackgroundWork.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Migration\FormBackgroundWork.Designer.cs">
<DependentUpon>FormBackgroundWork.cs</DependentUpon>
</Compile>
<Compile Include="Core\Handling\TweetDeckBridge.cs" />
@@ -126,10 +172,30 @@
<Compile Include="Core\Other\FormSettings.Designer.cs">
<DependentUpon>FormSettings.cs</DependentUpon>
</Compile>
<Compile Include="Core\Other\FormUpdateDownload.cs">
<Compile Include="Plugins\Controls\PluginControl.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Plugins\Controls\PluginControl.Designer.cs">
<DependentUpon>PluginControl.cs</DependentUpon>
</Compile>
<Compile Include="Plugins\Controls\PluginListFlowLayout.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Plugins\Controls\PluginListFlowLayout.Designer.cs">
<DependentUpon>PluginListFlowLayout.cs</DependentUpon>
</Compile>
<Compile Include="Plugins\Plugin.cs" />
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
<Compile Include="Plugins\PluginConfig.cs" />
<Compile Include="Plugins\PluginEnvironment.cs" />
<Compile Include="Plugins\PluginGroup.cs" />
<Compile Include="Plugins\Events\PluginLoadEventArgs.cs" />
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Plugins\PluginScriptGenerator.cs" />
<Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Core\Other\FormUpdateDownload.Designer.cs">
<Compile Include="Updates\FormUpdateDownload.Designer.cs">
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
</Compile>
<Compile Include="Core\TrayIcon.cs">
@@ -138,8 +204,14 @@
<Compile Include="Core\TrayIcon.Designer.cs">
<DependentUpon>TrayIcon.cs</DependentUpon>
</Compile>
<Compile Include="Core\Utils\BrowserCache.cs" />
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\UpdateInfo.cs" />
<Compile Include="Core\Utils\HardwareAcceleration.cs" />
<Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\UpdateAcceptedEventArgs.cs" />
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Migration\FormMigrationQuestion.cs">
<SubType>Form</SubType>
</Compile>
@@ -147,7 +219,6 @@
<DependentUpon>FormMigrationQuestion.cs</DependentUpon>
</Compile>
<Compile Include="Migration\Helpers\LnkEditor.cs" />
<Compile Include="Migration\Helpers\ProgramProcessSearch.cs" />
<Compile Include="Migration\MigrationDecision.cs" />
<Compile Include="Migration\MigrationManager.cs" />
<Compile Include="Migration\Helpers\ProgramRegistrySearch.cs" />
@@ -195,25 +266,20 @@
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\code.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>code.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\icon.ico">
<TargetPath>icon.ico</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Core\FormBrowser.resx">
<DependentUpon>FormBrowser.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Core\Other\FormAbout.resx">
<DependentUpon>FormAbout.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Core\Other\FormPlugins.resx">
<DependentUpon>FormPlugins.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Core\Other\FormSettings.resx">
<DependentUpon>FormSettings.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Core\Other\FormUpdateDownload.resx">
<EmbeddedResource Include="Updates\FormUpdateDownload.resx">
<DependentUpon>FormUpdateDownload.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
@@ -223,19 +289,47 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\notification.js">
<ContentWithTargetPath Include="Resources\icon.ico">
<TargetPath>icon.ico</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\code.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>code.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\notification.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>notification.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\update.js">
<ContentWithTargetPath Include="Resources\Scripts\plugins.browser.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>plugins.browser.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\plugins.notification.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>plugins.notification.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<ContentWithTargetPath Include="Resources\Scripts\update.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>update.js</TargetPath>
</ContentWithTargetPath>
</ItemGroup>
<ItemGroup>
<Content Include="Resources\icon-small.ico" />
<None Include="Resources\icon-small.ico" />
<None Include="Resources\icon-tray-new.ico" />
<None Include="Resources\icon-tray.ico" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\Plugins\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
@@ -258,7 +352,13 @@ del "$(TargetDir)LICENSE.txt"
ren "$(TargetDir)LICENSE.md" "LICENSE.txt"
xcopy "$(ProjectDir)Libraries\CEFSHARP-LICENSE.txt" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcp120.dll" "$(TargetDir)" /Y
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y</PostBuildEvent>
xcopy "$(ProjectDir)packages\Microsoft.VC120.CRT.JetBrains.12.0.21005.2\DotFiles\msvcr120.dll" "$(TargetDir)" /Y
mkdir "$(TargetDir)plugins"
mkdir "$(TargetDir)plugins\official"
mkdir "$(TargetDir)plugins\user"
xcopy "$(ProjectDir)Resources\Plugins\*" "$(TargetDir)plugins\official\" /E /Y
rmdir "$(ProjectDir)\bin\Debug"
rmdir "$(ProjectDir)\bin\Release"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

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

View File

@@ -1,13 +1,16 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Net;
using System.Windows.Forms;
using TweetDck.Core.Controls;
using TweetDck.Core.Utils;
namespace TweetDck.Core.Other{
namespace TweetDck.Updates{
sealed partial class FormUpdateDownload : Form{
private const double BytesToMB = 1024.0*1024.0;
public string InstallerPath{
get{
return Path.Combine(Path.GetTempPath(),updateInfo.FileName);
@@ -66,7 +69,7 @@ namespace TweetDck.Core.Other{
progressDownload.SetValueInstant(1000);
}
labelStatus.Text = (e.BytesReceived/(1024.0*1024.0)).ToString("0.0")+" MB";
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" MB";
}
else{
if (progressDownload.Style != ProgressBarStyle.Continuous){
@@ -74,7 +77,7 @@ namespace TweetDck.Core.Other{
}
progressDownload.SetValueInstant(e.ProgressPercentage*10);
labelStatus.Text = (e.BytesReceived/(1024.0*1024.0)).ToString("0.0")+" / "+(e.TotalBytesToReceive/(1024.0*1024.0)).ToString("0.0")+" MB";
labelStatus.Text = (e.BytesReceived/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" / "+(e.TotalBytesToReceive/BytesToMB).ToString("0.0",CultureInfo.CurrentCulture)+" MB";
}
});
}

View File

@@ -0,0 +1,377 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA
AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5IrF+OlS+hjyMw4g7tcmM
PM/MjTzezY485s2OPObMjjzfyIw80MOJPLe8hDyNs387WKx8OyQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHU4FrB8OGXGijzAzo889dKR
PP/SkTz/0pE8/9GRPP/RkDz/0JA8/9CQPP/RkDz/0ZE8/9KRPP/TkTz/0pE8/86PPPbGijzCt4E8bKp6
OxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKN1OB+9hTuLzI486NOR
PP/SkTz/0ZA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
PP/SkTz/0pE8/82OPOu9hTuOq3w8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACidTkFuoM8csyN
POrTkjz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GRPP/Tkjz/zY887bqEPHimeDsHAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKp6
OynDiDvA05I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZA8/9ORPP/GijzFr308LgAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAArXw7UM2OPOvTkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/SkTz/zo887rOAPFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAACxfjxm0ZA8/NGRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9GQPP64gjxtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAALaBPGXRkTz/0ZE8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQPP/SkTz/uIM8bAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqnk6TtCQPPzRkTz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/0ZA8/rSA
PFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtfDwmzo887dKRPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0ZE8/86PPPGtfDwrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZ4PAXFijzC0pE8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CURf/TpGT/2baJ/9/GpP/l0bf/59W//+fVvf/kz7P/3sOg/9mz
hP/ToWH/0JND/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/EiTvJo3Y7CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALiC
PG/Tkjz/0JA8/9CQPP/QkDz/0JA8/9CSQf/Vq3P/5tO7//j07v//////////////////////////////
////////////////////////9vHq/+TQtf/UqG7/0JA+/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/uYM8dgAAAAAAAAAAAAAAAAAA
AAAAAAAArXw8Gc2OPOzRkTz/0JA8/9CQPP/QlEX/27qP//fz7P//////////////////////////////
////////////////////////////////////////////////////////8ObZ/9auev/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/zI488J1x
OB8AAAAAAAAAAAAAAAAAAAAAuoM7h9OSPP/QkDz/0JA8/9GXTf/TpGb/27uS/9u6kP/ewZz/59W9//Xv
5///////////////////////////////////////////////////////////////////////////////
///r3cr/0ZxV/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/1JI8/7mDO5AAAAAAAAAAAAAAAADFj0cR0pNB6s+PO//Ojjv/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CRQP/TpGf/5NC2//38+v//////////////////////////////////////////////
////////////////////////+vf0/9auev/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0ZE8/8uOPO6SajYVAAAAAAAAAADzr1df7qxV/+WkT//ZmUT/0JA9/86O
O//Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GaUv/l07r/////////////////////////
///////////////////////////////////////////////////buo//0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9OSPP+ldjdoAAAAAAAAAADurFW67qxV//Ct
Vv/vrVb/6adR/92dSP/Skz//zo47/8+PO//QkDz/0JA8/9CQPP/QkDz/0JJB/9Smaf/dv5j/+fXw////
////////////////////////////////////////////////////////////////////////2riM/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KRPP/FiTzGoHQ6Ae6s
VRrurFXz7qxV/+6sVf/urFX/761W//CuVv/sqlT/4qFM/9aVQv/Pjzv/zo47/9CQPP/VqnH/8+vh////
////////////////////////////////////////////////////////////////////////////////
/////////////9aueP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9GQ
PP/Ojzz4oHM5Iu6sVU3urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/vrVX/8K5W/+6sVf/mpU//2plF/9q6
j///////////////////////////////////////////////////////////////////////////////
//////////////////////////////n18P/Rm1P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/SkTz/rXs6Vu6sVYPurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/wrVb/6r2B////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////o18L/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/v4Y8j+6sVa3urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFb/9Onb///////+/v3/////////////////////////////////////////
////////////////////////////////////////////////////////////////////////1qx2/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/wYc7uO6sVcrurFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/trlr/67Zy/+m+h//y5NP/////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////7+TW/9CQPf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80e6s
VdvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6sme//v49P//////////////
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////9arc//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/RkDz/zI083+6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/u2b3/////////
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////+fVvv/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVeLurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+vL
of//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////r28v/Rl0r/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/zo885u6sVdrurFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7LBj//r28f//////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///Vq3P/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkDz/zI083+6sVcrurFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/6cKO////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///////////////////ewp7/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/RkTz/yYw80O6s
VazurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7NOy//z59v/u2b7/+fPr////////////////////
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////n1sD/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/SkTz/xIo8uO6sVYHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/67Zx/+2uWf/qy6H/////////
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////v5dj/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/SkTz/vYU8je6sVUvurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nC
kP///////////////////////////////////////////////////////fz6//fu5P/w4Mv/793F//7+
/v/////////////////////////////////////////////////////////////////17eT/0JA//86O
Ov/Pjzv/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/tYA8Vu6sVRnurFXy7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7a5b//fw5///////////////////////////////////////+/jz/+7av//pwIr/67Jn/+6t
Vv/urFX/6rd1////////////////////////////////////////////////////////////////////
///48ej/6KhU/92cR//Tkj7/zo47/8+PO//QkDz/0JA8/9GQPP/Pjzz3rXw8IAAAAADurFW37qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/6b2D//////////////////////////////////nz6//qyZ3/7K9h/+6s
Vf/urFX/7qxV/+6sVf/urFX/6rl6////////////////////////////////////////////////////
///////////////////9/Pr/6r6C//CuV//sqlT/4aBL/9aWQv/Pjzv/zo47/9GRPP/GijzCp3g8AQAA
AADurFVb7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/68yk///////////////////////+/fz/68+q/+2v
XP/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/6rNr//7+/f//////////////////////////////
/////////////////////////////////////////////+nEk//vrVX/8K5W/+6sVf/lpE//2ZlF/9KR
Pf+zfzpoAAAAAAAAAADurFUP7qxV5u6sVf/urFX/7qxV/+6sVf/urFX/7NGu//////////////////fu
5P/quHb/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxW//Xr3///////////////
///////////////////////////////////////////////////7+PP/+vXu///////pxJL/7qxV/+6s
Vf/wrVb/8K1W/+mnUevUmUsUAAAAAAAAAAAAAAAA7qxVgO6sVf/urFX/7qxV/+6sVf/urFX/6sqg////
////////8eLO/+yvX//urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nF
lf/////////////////////////////////////////////////////////////////8+vf/6cWV/+q5
ev/s07H/6rd1/+6sVf/urFX/7qxV/++tVogAAAAAAAAAAAAAAAAAAAAA7qxVFe6sVejurFX/7qxV/+6s
Vf/urFX/6bp8///////w4Mn/7a1Z/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/x4cz////////////////////////////////////////////////////+//7+
/v/////////+/+m8gP/urFX/7a1Y/+6sVf/urFX/7qxV7O6sVRoAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
VWburFX/7qxV/+6sVf/urFX/7a5b/+zTs//sr1//7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/sr1//8uPQ////////////////////////////////////
////////6suj/+qzav/pxpj/9Onb//n07f/rr2D/7qxV/+6sVf/urFX/7qxVbwAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAO6sVQPurFW67qxV/+6sVf/urFX/7qxV/+6sVv/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7axY/+nGl//27uP/////////
/////////fz6//Lj0P/qunv/7qxV/+6sVf/urFX/7qxV/+m9gv/qt3X/7qxV/+6sVf/urFXB7qxVBQAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADurFUg7qxV6O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/qtnP/6sqg/+3WuP/s07P/6cSS/+uyZf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVv/trVr/7qxV/+6s
VezurFUlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVRu6sVfjurFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV++6sVUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6s
VVzurFX97qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxVYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAADurFVd7qxV+O6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfvurFVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVR+6sVeXurFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV6O6sVU0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSPurFW37qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFW77qxVJgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADurFUB7qxVae6sVeTurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV5+6sVW3urFUDAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVRjurFWA7qxV4u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeTurFWE7qxVGwAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVEu6sVV7urFW17qxV7+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVfDurFW47qxVX+6s
VRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA7qxVG+6sVUzurFWA7qxVrO6sVcnurFXa7qxV4u6sVeLurFXa7qxVye6sVa3urFWC7qxVTe6s
VR0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAP//gAH//6gr//wAAD//qCv/8AAAD/+oK//AAAAD/6gr/4AAAAH/qCv/AAAAAP+oK/4A
AAAAf6gr/AAAAAA/qCv4AAAAAB+oK/AAAAAAD6gr4AAAAAAHqCvgAAAAAAeoK8AAAAAAA6grwAAAAAAD
qCuAAAAAAAGoK4AAAAAAAagrgAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAA
AAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAAqCsAAAAAAACoKwAAAAAAAKgrAAAAAAAA
qCsAAAAAAACoKwAAAAAAAKgrgAAAAAAAqCuAAAAAAAGoK4AAAAAAAagrwAAAAAADqCvAAAAAAAOoK+AA
AAAAB6gr4AAAAAAHqCvwAAAAAA+oK/gAAAAAH6gr/AAAAAA/qCv+AAAAAH+oK/8AAAAA/6gr/4AAAAH/
qCv/wAAAA/+oK//wAAAP/6gr//wAAD//qCv//4AB//+oKygAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApHY4FrR/
Ole+hjuVxos7w8uNPNvOjzznzo8858uNPNzGijzEvoU7l7WBO1mpejsYAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHg5Kr6F
O5XLjTvo0pE8/9ORPP/SkTz/0ZE8/9CQPP/QkDz/0ZE8/9KRPP/TkTz/0pE8/82OPOq/hjyZrXw7LAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3U6CbuE
O4bPjzz005I8/9GRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0ZE8/9OS
PP/PkDz1vYU8iah6PAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKR3
OibGizvG05E8/9GQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9GQPP/TkTz/yIs8yqx8PCkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AACsezsxy40849KRPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485rKAPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAApXc6JMuNPOTSkTz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/zI485659PCcAAAAAAAAAAAAA
AAAAAAAAAAAAAKd4OwnHizzJ0pE8/9CQPP/QkDz/0JA9/9OgYP/YtIX/38Sh/+LMr//iy67/3sOf/9iy
gv/Tn13/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8zaV4
OwsAAAAAAAAAAAAAAAAAAAAAu4Q8hNOSPP/QkDz/0JA8/9GXS//YtIX/6NfC//Xv5//8+vj/////////
///8+vf/9O3k/+fVvv/YsYH/0JRF/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/Tkjz/u4Q7iQAAAAAAAAAAAAAAAKx7OyTPjzz10ZA8/9GXS//Ztoj/8une////////////////////
///////////////////////////////////38uz/3L2V/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9GRPP/Ojzz3onU5KQAAAAAAAAAAxo1BlNGQO//Ojjv/0JNC/9GWSP/QlET/0ZpR/9au
eP/n1b3//v79////////////////////////////////////////////7+TV/9GdWP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/0JA8/9SSPP+4gjqaAAAAAPGuVhHvrVXq5qRO/9qZRP/QkT3/zo46/8+P
O//QkDz/0JA8/9CQPP/To2P/8+vh////////////////////////////////////////////+fXw/9Oh
Yf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/0pE8/8qMO+2WbTYV7qxVUe6sVf/wrVb/761W/+mo
Uv/enUj/05M//86PO//PkD7/3cCZ//Lp3v/7+PT/////////////////////////////////////////
////////+PTv/9GcVf/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/QkDz/05I8/699OljurFWR7qxV/+6s
Vf/urFX/761W//CuV//sqlT/4qJQ/+zdyf//////////////////////////////////////////////
////////////////////////7+TV/9CRP//QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Tkjz/vIQ7mO6s
Vb/urFX/7qxV/+6sVf/urFX/7qxV/++tVf/ry5///v79//7+/f//////////////////////////////
////////////////////////////////////////2rqO/9CQPP/QkDz/0JA8/9CQPP/QkDz/0JA8/9KR
PP/GijvF7qxV2u6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u1bv/t1bb//fv4////////////////////
///////////////////////////////////////////////////48+3/0ZRG/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0ZA8/8uNPNzurFXl7qxV/+6sVf/urFX/7qxV/+6sVf/rsWP/9OjZ////////////////////
///////////////////////////////////////////////////////////////////YtIX/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/QkDz/zo886O6sVeXurFX/7qxV/+6sVf/urFX/7qxW//Hiz///////////////
/////////////////////////////////////////////////////////////////////////////+rb
yP/QkDz/0JA8/9CQPP/QkDz/0JA8/9CQPP/Ojzzo7qxV2e6sVf/urFX/7qxV/+6sVf/quHb/////////
////////////////////////////////////////////////////////////////////////////////
////////+fXw/9CURf/QkDz/0JA8/9CQPP/QkDz/0ZA8/8yOPNzurFW/7qxV/+6sVf/urFX/7qxV/+nA
if/s1LT/9Onb////////////////////////////////////////////////////////////////////
////////////////////////0Z5b/9CQPP/QkDz/0JA8/9CQPP/SkTz/x4s8xe6sVY/urFX/7qxV/+6s
Vf/urFX/7qxV/+nEk//+/fz////////////////////////////+/v3/+PHo//To2f/+/v7/////////
///////////////////////////////////VqGz/zo46/8+PO//QkDz/0JA8/9KRPP+/hjyX7qxVUO6s
Vf/urFX/7qxV/+6sVf/ssGH//Pr2///////////////////////y49D/6cOR/+qyaf/urVb/6rJp////
/////////////////////////////////////////////+e7f//goEr/1ZVB/8+PO//Ojzv/0pE8/7WB
PFfurFUQ7qxV6O6sVf/urFX/7qxV/+m+hv/////////////////z5tX/6rl6/+6sVf/urFX/7qxV/+6s
Vf/qs2r////+////////////////////////////////////////////+PLp/+y4c//urFX/5aRO/9qZ
RP/Mjjztp3g6FQAAAADurFWQ7qxV/+6sVf/urFX/6cKP////////////682m/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+2sV//17N////////////////////////////////////////7+/v/06Nn/8uTT/+qz
a//wrVb/761W/+alUZYAAAAAAAAAAO6sVSHurFX07qxV/+6sVf/quXj//////+nDkf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+m+hP////////////////////////////////////////////jy
6v/qtnH/67Fj/+6sVf/urFX2761WJQAAAAAAAAAAAAAAAO6sVX7urFX/7qxV/+2vW//pvID/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+nEkv/7+PT//////////////////fz6/+rJ
n//rtW7/682n/+zTs//urFb/7qxV/+6sVYQAAAAAAAAAAAAAAAAAAAAA7qxVB+6sVcTurFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+u0bP/qy6H/7da4/+vN
pv/qtnP/7qxV/+6sVf/urFX/7K9f/+2sWP/urFXI7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVIO6s
VeDurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV4+6sVSMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAA7qxVLO6sVd/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVeLurFUwAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA7qxVIu6sVcHurFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXE7qxVJAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7qxVBu6sVX/urFXw7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXy7qxVgu6sVQgAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVSXurFWP7qxV5e6s
Vf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFXl7qxVke6sVScAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AADurFUS7qxVUO6sVY7urFW97qxV2O6sVeXurFXl7qxV2O6sVb7urFWP7qxVUu6sVRMAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AD//8AAP/8AAA/+AAAH/AAAA/gAAAHwAAAA8AA
AAOAAAABgAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAA
AAGAAAABwAAAA8AAAAPgAAAH8AAAD/gAAB/8AAA//wAA///AA/8oAAAAEAAAACAAAAABACAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKx7Oja9hTqTyYw8zs6PPOfOjzznyYw8zr6G
PJSvfjw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo3Y7CbyEO5PRkDz805I8/9GRPP/QkDz/0JA8/9GR
PP/TkTz/0ZA8/b+GPJWrfDwKAAAAAAAAAAAAAAAAp3g8CcSJPLfTkjz/0ZA8/9CQPP/QkDz/0JA8/9CQ
PP/QkDz/0JA8/9CQPP/Tkjz/xoo8uqx8PQoAAAAAAAAAAL2FPJPUk0D/06Rm/92/mf/izK//4sut/9u6
j//Snlr/0JA8/9CQPP/QkDz/0JA8/9OSPP+9hTyWAAAAALiEQDLQkDz90JpS/93Amf/u4tP/////////
/////////fv5/+DIqP/QkkH/0JA8/9CQPP/RkDz/0JA8/qN1OTTtq1WS56VP/9qaRf/RkT3/0JdO/+rc
yP//////////////////////693L/9CSQf/QkDz/0JA8/9SSPP+3gTqW761Vz++tVv/wrVb/6K5h//fy
6v/////////////////////////////////gx6b/0JA8/9CQPP/RkTz/yYw80e6sVefurFX/7qxV/+q+
hP/69vD//////////////////////////////////fz7/9GbU//QkDz/0JA8/8+PPOnurFXn7qxV/+yw
Y//79/P////////////////////////////////////////////ZuIz/0JA8/9CQPP/Pjzzp7qxVz+6s
Vf/sr2H/8eLO/////////////v7+//n07f/+/v7/////////////////48yu/8+PO//Qjzv/yYs80e6s
VZLurFX/67Jm//7+/f/8+fb/68yl/+qza//rsmf//v37//////////////////Pn2P/ip1n/2phC/8CH
PJXurFUw7qxV/eq2c//169//6rNr/+6sVf/urFX/7q1W//Pm1f/////////////////8+fX/67+G/++t
Vf3lpVEyAAAAAO6sVZHtrVr/7a9c/+6sVf/urFX/7qxV/+6sVf/rsWP/7de5//Lj0P/pxJL/67Zy/+qz
av/urFWTAAAAAAAAAADurFUI7qxVtO6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
Vf/urFW37qxVCQAAAAAAAAAAAAAAAO6sVQjurFWQ7qxV++6sVf/urFX/7qxV/+6sVf/urFX/7qxV/+6s
VfvurFWS7qxVCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO6sVTPurFWQ7qxVzO6sVeburFXm7qxVzO6s
VZDurFU0AAAAAAAAAAAAAAAAAAAAAPAPrEHAA6xBgAGsQYABrEEAAKxBAACsQQAArEEAAKxBAACsQQAA
rEEAAKxBAACsQYABrEGAAaxBwAOsQfAPrEE=
</value>
</data>
</root>

View File

@@ -0,0 +1,11 @@
using System;
namespace TweetDck.Updates{
class UpdateAcceptedEventArgs : EventArgs{
public readonly UpdateInfo UpdateInfo;
public UpdateAcceptedEventArgs(UpdateInfo info){
this.UpdateInfo = info;
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
namespace TweetDck.Updates{
class UpdateCheckEventArgs : EventArgs{
public int EventId { get; private set; }
public bool UpdateAvailable { get; private set; }
public string LatestVersion { get; private set; }
public UpdateCheckEventArgs(int eventId, bool updateAvailable, string latestVersion){
EventId = eventId;
UpdateAvailable = updateAvailable;
LatestVersion = latestVersion;
}
}
}

95
Updates/UpdateHandler.cs Normal file
View File

@@ -0,0 +1,95 @@
using System;
using CefSharp;
using CefSharp.WinForms;
using TweetDck.Core;
using TweetDck.Core.Controls;
using TweetDck.Resources;
namespace TweetDck.Updates{
class UpdateHandler{
private readonly ChromiumWebBrowser browser;
private readonly FormBrowser form;
public event EventHandler<UpdateAcceptedEventArgs> UpdateAccepted;
public event EventHandler<UpdateCheckEventArgs> CheckFinished;
private int lastEventId;
public UpdateHandler(ChromiumWebBrowser browser, FormBrowser form){
this.browser = browser;
this.form = form;
browser.FrameLoadEnd += browser_FrameLoadEnd;
browser.RegisterJsObject("$TDU",new Bridge(this));
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain){
ScriptLoader.ExecuteFile(e.Frame,"update.js");
}
}
public int Check(bool force){
browser.ExecuteScriptAsync("TDUF_runUpdateCheck",force,++lastEventId);
return lastEventId;
}
private void TriggerUpdateAcceptedEvent(UpdateAcceptedEventArgs args){
if (UpdateAccepted != null){
form.InvokeSafe(() => UpdateAccepted(this,args));
}
}
private void TriggerCheckFinishedEvent(UpdateCheckEventArgs args){
if (CheckFinished != null){
form.InvokeSafe(() => CheckFinished(this,args));
}
}
public class Bridge{
public string BrandName{
get{
return Program.BrandName;
}
}
public string VersionTag{
get{
return Program.VersionTag;
}
}
public bool UpdateCheckEnabled{
get{
return Program.UserConfig.EnableUpdateCheck;
}
}
public string DismissedVersionTag{
get{
return Program.UserConfig.DismissedUpdate ?? string.Empty;
}
}
private readonly UpdateHandler owner;
public Bridge(UpdateHandler owner){
this.owner = owner;
}
public void OnUpdateCheckFinished(int eventId, bool isUpdateAvailable, string latestVersion){
owner.TriggerCheckFinishedEvent(new UpdateCheckEventArgs(eventId,isUpdateAvailable,latestVersion));
}
public void OnUpdateAccepted(string versionTag, string downloadUrl){
owner.TriggerUpdateAcceptedEvent(new UpdateAcceptedEventArgs(new UpdateInfo(versionTag,downloadUrl)));
}
public void OnUpdateDismissed(string versionTag){
owner.form.InvokeSafe(() => {
Program.UserConfig.DismissedUpdate = versionTag;
Program.UserConfig.Save();
});
}
}
}
}

View File

@@ -1,4 +1,6 @@
namespace TweetDck.Core.Utils{
using TweetDck.Core.Utils;
namespace TweetDck.Updates{
class UpdateInfo{
public readonly string VersionTag;
public readonly string DownloadUrl;

View File

@@ -2,3 +2,8 @@ del "bin\x86\Release Dick\*.xml"
del "bin\x86\Release Dick\devtools_resources.pak"
del "bin\x86\Release Duck\*.xml"
del "bin\x86\Release Duck\devtools_resources.pak"
del "bin\x86\Release Dick\TweetDick.Browser.exe"
ren "bin\x86\Release Dick\CefSharp.BrowserSubprocess.exe" "TweetDick.Browser.exe"
del "bin\x86\Release Duck\TweetDuck.Browser.exe"
ren "bin\x86\Release Duck\CefSharp.BrowserSubprocess.exe" "TweetDuck.Browser.exe"