mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-04-28 00:15:47 +02:00
371 lines
13 KiB
C#
371 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using CefSharp;
|
|
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 ChromiumWebBrowser browser;
|
|
|
|
private readonly Queue<TweetNotification> tweetQueue = new Queue<TweetNotification>(4);
|
|
private readonly bool autoHide;
|
|
private int timeLeft, totalTime;
|
|
|
|
private readonly NativeMethods.HookProc mouseHookDelegate;
|
|
private IntPtr mouseHook;
|
|
|
|
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{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public bool FreezeTimer { get; set; }
|
|
public bool ContextMenuOpen { get; set; }
|
|
public string CurrentUrl { get; private set; }
|
|
public string CurrentQuotedTweetUrl { get; set; }
|
|
|
|
public EventHandler Initialized;
|
|
private bool isInitialized;
|
|
|
|
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 pluginManager, bool autoHide){
|
|
InitializeComponent();
|
|
|
|
Text = Program.BrandName;
|
|
|
|
this.owner = owner;
|
|
this.plugins = pluginManager;
|
|
this.autoHide = autoHide;
|
|
|
|
owner.FormClosed += (sender, args) => Close();
|
|
|
|
notificationJS = ScriptLoader.LoadResource(NotificationScriptFile);
|
|
pluginJS = ScriptLoader.LoadResource(PluginManager.PluginNotificationScriptFile);
|
|
|
|
browser = new ChromiumWebBrowser("about:blank"){
|
|
MenuHandler = new ContextMenuNotification(this, autoHide),
|
|
LifeSpanHandler = new LifeSpanHandler()
|
|
};
|
|
|
|
browser.IsBrowserInitializedChanged += Browser_IsBrowserInitializedChanged;
|
|
browser.FrameLoadEnd += Browser_FrameLoadEnd;
|
|
browser.RegisterJsObject("$TD", new TweetDeckBridge(owner, this));
|
|
browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
|
|
|
|
panelBrowser.Controls.Add(browser);
|
|
|
|
if (autoHide){
|
|
Program.UserConfig.MuteToggled += Config_MuteToggled;
|
|
Disposed += (sender, args) => Program.UserConfig.MuteToggled -= Config_MuteToggled;
|
|
}
|
|
|
|
mouseHookDelegate = MouseHookProc;
|
|
|
|
Disposed += FormNotification_Disposed;
|
|
}
|
|
|
|
protected override void WndProc(ref Message m){
|
|
if (m.Msg == 0x0112 && (m.WParam.ToInt32() & 0xFFF0) == 0xF010 && !CanMoveWindow()){ // WM_SYSCOMMAND, SC_MOVE
|
|
return;
|
|
}
|
|
|
|
base.WndProc(ref m);
|
|
}
|
|
|
|
// mouse wheel hook
|
|
|
|
private void StartMouseHook(){
|
|
if (mouseHook == IntPtr.Zero){
|
|
mouseHook = NativeMethods.SetWindowsHookEx(NativeMethods.WH_MOUSE_LL, mouseHookDelegate, IntPtr.Zero, 0);
|
|
}
|
|
}
|
|
|
|
private void StopMouseHook(){
|
|
if (mouseHook != IntPtr.Zero){
|
|
NativeMethods.UnhookWindowsHookEx(mouseHook);
|
|
mouseHook = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
private IntPtr MouseHookProc(int nCode, IntPtr wParam, IntPtr lParam){
|
|
if (!ContainsFocus && wParam.ToInt32() == NativeMethods.WH_MOUSEWHEEL && browser.Bounds.Contains(PointToClient(Cursor.Position))){
|
|
// fuck it, Activate() doesn't work with this
|
|
Point prevPos = Cursor.Position;
|
|
Cursor.Position = PointToScreen(new Point(0, -1));
|
|
NativeMethods.SimulateMouseClick(NativeMethods.MouseButton.Left);
|
|
Cursor.Position = prevPos;
|
|
}
|
|
|
|
return NativeMethods.CallNextHookEx(mouseHook, nCode, wParam, lParam);
|
|
}
|
|
|
|
// event handlers
|
|
|
|
private void timerHideProgress_Tick(object sender, EventArgs e){
|
|
if (Bounds.Contains(Cursor.Position) || FreezeTimer || ContextMenuOpen)return;
|
|
|
|
timeLeft -= timerProgress.Interval;
|
|
|
|
int value = (int)Math.Round(1025.0*(totalTime-timeLeft)/totalTime);
|
|
progressBarTimer.SetValueInstant(Math.Min(1000, Math.Max(0, Program.UserConfig.NotificationTimerCountDown ? 1000-value : value)));
|
|
|
|
if (timeLeft <= 0){
|
|
FinishCurrentTweet();
|
|
}
|
|
}
|
|
|
|
private void Config_MuteToggled(object sender, EventArgs e){
|
|
if (Program.UserConfig.MuteNotifications){
|
|
HideNotification(true);
|
|
}
|
|
else if (tweetQueue.Count > 0){
|
|
LoadNextNotification();
|
|
}
|
|
}
|
|
|
|
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)return;
|
|
|
|
if (!isInitialized && !Program.UserConfig.NotificationLegacyLoad){
|
|
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);
|
|
ScriptLoader.ExecuteFile(e.Frame, PluginManager.PluginGlobalScriptFile);
|
|
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Notification, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FormNotification_FormClosing(object sender, FormClosingEventArgs e){
|
|
if (e.CloseReason == CloseReason.UserClosing){
|
|
HideNotification(false);
|
|
tweetQueue.Clear();
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
private void FormNotification_Disposed(object sender, EventArgs e){
|
|
browser.Dispose();
|
|
StopMouseHook();
|
|
}
|
|
|
|
// notification methods
|
|
|
|
public void ShowNotification(TweetNotification notification){
|
|
if (Program.UserConfig.MuteNotifications){
|
|
tweetQueue.Enqueue(notification);
|
|
}
|
|
else{
|
|
tweetQueue.Enqueue(notification);
|
|
UpdateTitle();
|
|
|
|
if (totalTime == 0){
|
|
LoadNextNotification();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ShowNotificationForSettings(bool reset){
|
|
if (reset){
|
|
LoadTweet(TweetNotification.ExampleTweet);
|
|
}
|
|
else{
|
|
MoveToVisibleLocation();
|
|
}
|
|
}
|
|
|
|
public void HideNotification(bool loadBlank){
|
|
if (loadBlank || Program.UserConfig.NotificationLegacyLoad){
|
|
browser.LoadHtml("", "about:blank");
|
|
}
|
|
|
|
Location = new Point(-32000, -32000);
|
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
timerProgress.Stop();
|
|
totalTime = 0;
|
|
|
|
StopMouseHook();
|
|
}
|
|
|
|
public void OnNotificationReady(){
|
|
UpdateTitle();
|
|
MoveToVisibleLocation();
|
|
timerProgress.Start();
|
|
}
|
|
|
|
public void FinishCurrentTweet(){
|
|
if (tweetQueue.Count > 0){
|
|
LoadNextNotification();
|
|
}
|
|
else if (autoHide){
|
|
HideNotification(true);
|
|
}
|
|
else{
|
|
timerProgress.Stop();
|
|
}
|
|
}
|
|
|
|
private void LoadNextNotification(){
|
|
LoadTweet(tweetQueue.Dequeue());
|
|
}
|
|
|
|
private void LoadTweet(TweetNotification tweet){
|
|
CurrentUrl = tweet.Url;
|
|
CurrentQuotedTweetUrl = string.Empty; // load from JS
|
|
|
|
timerProgress.Stop();
|
|
totalTime = timeLeft = tweet.GetDisplayDuration(Program.UserConfig.NotificationDurationValue);
|
|
progressBarTimer.Value = Program.UserConfig.NotificationTimerCountDown ? 1000 : 0;
|
|
|
|
browser.LoadHtml(tweet.GenerateHtml(), "http://tweetdeck.twitter.com/?"+DateTime.Now.Ticks);
|
|
|
|
if (Program.UserConfig.NotificationLegacyLoad){
|
|
OnNotificationReady();
|
|
}
|
|
}
|
|
|
|
private void MoveToVisibleLocation(){
|
|
UserConfig config = Program.UserConfig;
|
|
|
|
if (RequiresResize){
|
|
RequiresResize = false;
|
|
|
|
if (config.DisplayNotificationTimer){
|
|
ClientSize = new Size(BaseClientWidth, BaseClientHeight+4);
|
|
progressBarTimer.Visible = true;
|
|
}
|
|
else{
|
|
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){
|
|
case TweetNotification.Position.TopLeft:
|
|
Location = new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+edgeDist);
|
|
break;
|
|
|
|
case TweetNotification.Position.TopRight:
|
|
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
|
break;
|
|
|
|
case TweetNotification.Position.BottomLeft:
|
|
Location = new Point(screen.WorkingArea.X+edgeDist, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
|
break;
|
|
|
|
case TweetNotification.Position.BottomRight:
|
|
Location = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+screen.WorkingArea.Height-edgeDist-Height);
|
|
break;
|
|
|
|
case TweetNotification.Position.Custom:
|
|
if (!config.IsCustomNotificationPositionSet){
|
|
config.CustomNotificationPosition = new Point(screen.WorkingArea.X+screen.WorkingArea.Width-edgeDist-Width, screen.WorkingArea.Y+edgeDist);
|
|
config.Save();
|
|
}
|
|
|
|
Location = config.CustomNotificationPosition;
|
|
break;
|
|
}
|
|
|
|
if (needsReactivating){
|
|
NativeMethods.SetFormPos(this, NativeMethods.HWND_TOPMOST, NativeMethods.SWP_NOACTIVATE);
|
|
}
|
|
|
|
StartMouseHook();
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|