From 442d74d0cb28e0fd20da320a1ace88cffc93e21e Mon Sep 17 00:00:00 2001 From: chylex <contact@chylex.com> Date: Sun, 27 Aug 2017 18:18:30 +0200 Subject: [PATCH] Refactor context menu handling and make adding new types of context easier --- Core/Bridge/TweetDeckBridge.cs | 13 +-- Core/Handling/ContextMenuBase.cs | 112 +++++++++++++---------- Core/Handling/ContextMenuBrowser.cs | 46 +++++----- Core/Handling/ContextMenuNotification.cs | 26 +++--- Resources/Scripts/code.js | 13 ++- 5 files changed, 110 insertions(+), 100 deletions(-) diff --git a/Core/Bridge/TweetDeckBridge.cs b/Core/Bridge/TweetDeckBridge.cs index 36409889..5fd2c53a 100644 --- a/Core/Bridge/TweetDeckBridge.cs +++ b/Core/Bridge/TweetDeckBridge.cs @@ -3,6 +3,7 @@ using System.Windows.Forms; using CefSharp; using TweetDuck.Core.Controls; +using TweetDuck.Core.Handling; using TweetDuck.Core.Notification; using TweetDuck.Core.Other; using TweetDuck.Core.Utils; @@ -10,8 +11,6 @@ namespace TweetDuck.Core.Bridge{ sealed class TweetDeckBridge{ - public static string LastRightClickedLink = string.Empty; - public static string LastRightClickedImage = string.Empty; public static string LastHighlightedTweet = string.Empty; public static string LastHighlightedQuotedTweet = string.Empty; public static string LastHighlightedTweetAuthor = string.Empty; @@ -19,7 +18,7 @@ sealed class TweetDeckBridge{ public static Dictionary<string, string> SessionData = new Dictionary<string, string>(2); public static void ResetStaticProperties(){ - LastRightClickedLink = LastRightClickedImage = LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty; + LastHighlightedTweet = LastHighlightedQuotedTweet = LastHighlightedTweetAuthor = string.Empty; LastHighlightedTweetImages = StringUtils.EmptyArray; } @@ -56,12 +55,8 @@ public void LoadNotificationHeadContents(string headContents){ }); } - public void SetLastRightClickedLink(string link){ - form.InvokeAsyncSafe(() => LastRightClickedLink = link); - } - - public void SetLastRightClickedImage(string link){ - form.InvokeAsyncSafe(() => LastRightClickedImage = link); + public void SetLastRightClickInfo(string type, string link){ + form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link)); } public void SetLastHighlightedTweet(string link, string quotedLink, string author, string imageList){ diff --git a/Core/Handling/ContextMenuBase.cs b/Core/Handling/ContextMenuBase.cs index 6d78b3bd..9765c3e3 100644 --- a/Core/Handling/ContextMenuBase.cs +++ b/Core/Handling/ContextMenuBase.cs @@ -6,29 +6,35 @@ using TweetDuck.Core.Bridge; using TweetDuck.Core.Controls; using TweetDuck.Core.Utils; +using System.Collections.Generic; namespace TweetDuck.Core.Handling{ abstract class ContextMenuBase : IContextMenuHandler{ protected static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak")); private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality; + + private static KeyValuePair<string, string> ContextInfo; + private static bool IsLink => ContextInfo.Key == "link"; + private static bool IsImage => ContextInfo.Key == "image"; + private static bool IsVideo => ContextInfo.Key == "video"; - private static string GetLink(IContextMenuParams parameters){ - return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedLink) ? parameters.UnfilteredLinkUrl : TweetDeckBridge.LastRightClickedLink; + public static void SetContextInfo(string type, string link){ + ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link); } - private static string GetImage(IContextMenuParams parameters){ - return string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage) ? parameters.SourceUrl : TweetDeckBridge.LastRightClickedImage; + private static string GetMediaLink(IContextMenuParams parameters){ + return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl; } - private const int MenuOpenLinkUrl = 26500; - private const int MenuCopyLinkUrl = 26501; - private const int MenuCopyUsername = 26502; - private const int MenuOpenImageUrl = 26503; - private const int MenuCopyImageUrl = 26504; - private const int MenuSaveImage = 26505; - private const int MenuSaveAllImages = 26506; - private const int MenuOpenDevTools = 26599; + private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500; + private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501; + private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502; + private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26503; + private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26504; + private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26505; + private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26506; + private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; private readonly Form form; @@ -40,36 +46,46 @@ protected ContextMenuBase(Form form){ } public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ - bool hasTweetImage = !string.IsNullOrEmpty(TweetDeckBridge.LastRightClickedImage); - lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor; - lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages; - if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ lastHighlightedTweetAuthor = string.Empty; lastHighlightedTweetImageList = StringUtils.EmptyArray; + ContextInfo = default(KeyValuePair<string, string>); + } + else{ + lastHighlightedTweetAuthor = TweetDeckBridge.LastHighlightedTweetAuthor; + lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImages; } - if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage){ + bool hasTweetImage = IsImage; + bool hasTweetVideo = IsVideo; + + string TextOpen(string name) => "Open "+name+" in browser"; + string TextCopy(string name) => "Copy "+name+" address"; + string TextSave(string name) => "Save "+name+" as..."; + + if (parameters.TypeFlags.HasFlag(ContextMenuType.Link) && !parameters.UnfilteredLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal) && !hasTweetImage && !hasTweetVideo){ if (TwitterUtils.RegexAccount.IsMatch(parameters.UnfilteredLinkUrl)){ - model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open account in browser"); - model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy account address"); - model.AddItem((CefMenuCommand)MenuCopyUsername, "Copy account username"); + model.AddItem(MenuOpenLinkUrl, TextOpen("account")); + model.AddItem(MenuCopyLinkUrl, TextCopy("account")); + model.AddItem(MenuCopyUsername, "Copy account username"); } else{ - model.AddItem((CefMenuCommand)MenuOpenLinkUrl, "Open link in browser"); - model.AddItem((CefMenuCommand)MenuCopyLinkUrl, "Copy link address"); + model.AddItem(MenuOpenLinkUrl, TextOpen("link")); + model.AddItem(MenuCopyLinkUrl, TextCopy("link")); } model.AddSeparator(); } - if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){ - model.AddItem((CefMenuCommand)MenuOpenImageUrl, "Open image in browser"); - model.AddItem((CefMenuCommand)MenuCopyImageUrl, "Copy image address"); - model.AddItem((CefMenuCommand)MenuSaveImage, "Save image as..."); + if (hasTweetVideo){ + } + else if ((parameters.TypeFlags.HasFlag(ContextMenuType.Media) && parameters.HasImageContents) || hasTweetImage){ + model.AddItem(MenuOpenMediaUrl, TextOpen("image")); + model.AddItem(MenuCopyMediaUrl, TextCopy("image")); + model.AddItem(MenuSaveMedia, TextSave("image")); if (lastHighlightedTweetImageList.Length > 1){ - model.AddItem((CefMenuCommand)MenuSaveAllImages, "Save all images as..."); + model.AddItem(MenuSaveTweetImages, TextSave("all images")); } model.AddSeparator(); @@ -77,35 +93,36 @@ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser bro } public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ - switch((int)commandId){ + switch(commandId){ case MenuOpenLinkUrl: BrowserUtils.OpenExternalBrowser(parameters.LinkUrl); break; case MenuCopyLinkUrl: - SetClipboardText(GetLink(parameters)); - break; - - case MenuOpenImageUrl: - BrowserUtils.OpenExternalBrowser(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality)); - break; - - case MenuSaveImage: - TwitterUtils.DownloadImage(GetImage(parameters), lastHighlightedTweetAuthor, ImageQuality); - break; - - case MenuSaveAllImages: - TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality); - break; - - case MenuCopyImageUrl: - SetClipboardText(TwitterUtils.GetImageLink(GetImage(parameters), ImageQuality)); + SetClipboardText(IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl); break; case MenuCopyUsername: Match match = TwitterUtils.RegexAccount.Match(parameters.UnfilteredLinkUrl); SetClipboardText(match.Success ? match.Groups[1].Value : parameters.UnfilteredLinkUrl); break; + + case MenuOpenMediaUrl: + BrowserUtils.OpenExternalBrowser(TwitterUtils.GetImageLink(GetMediaLink(parameters), ImageQuality)); + break; + + case MenuCopyMediaUrl: + SetClipboardText(TwitterUtils.GetImageLink(GetMediaLink(parameters), ImageQuality)); + break; + + case MenuSaveMedia: + TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthor, ImageQuality); + + break; + + case MenuSaveTweetImages: + TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthor, ImageQuality); + break; case MenuOpenDevTools: browserControl.ShowDevTools(); @@ -116,8 +133,7 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br } public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ - TweetDeckBridge.LastRightClickedLink = string.Empty; - TweetDeckBridge.LastRightClickedImage = string.Empty; + ContextInfo = default(KeyValuePair<string, string>); } public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ @@ -129,7 +145,7 @@ protected void SetClipboardText(string text){ } protected static void AddDebugMenuItems(IMenuModel model){ - model.AddItem((CefMenuCommand)MenuOpenDevTools, "Open dev tools"); + model.AddItem(MenuOpenDevTools, "Open dev tools"); } protected static void RemoveSeparatorIfLast(IMenuModel model){ diff --git a/Core/Handling/ContextMenuBrowser.cs b/Core/Handling/ContextMenuBrowser.cs index 69ac45b6..fed0e03e 100644 --- a/Core/Handling/ContextMenuBrowser.cs +++ b/Core/Handling/ContextMenuBrowser.cs @@ -6,17 +6,17 @@ namespace TweetDuck.Core.Handling{ class ContextMenuBrowser : ContextMenuBase{ - private const int MenuGlobal = 26600; - private const int MenuMute = 26601; - private const int MenuSettings = 26602; - private const int MenuPlugins = 26003; - private const int MenuAbout = 26604; + private const CefMenuCommand MenuGlobal = (CefMenuCommand)26600; + private const CefMenuCommand MenuMute = (CefMenuCommand)26601; + private const CefMenuCommand MenuSettings = (CefMenuCommand)26602; + private const CefMenuCommand MenuPlugins = (CefMenuCommand)26003; + private const CefMenuCommand MenuAbout = (CefMenuCommand)26604; - private const int MenuOpenTweetUrl = 26610; - private const int MenuCopyTweetUrl = 26611; - private const int MenuOpenQuotedTweetUrl = 26612; - private const int MenuCopyQuotedTweetUrl = 26613; - private const int MenuScreenshotTweet = 26614; + private const CefMenuCommand MenuOpenTweetUrl = (CefMenuCommand)26610; + private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26611; + private const CefMenuCommand MenuOpenQuotedTweetUrl = (CefMenuCommand)26612; + private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26613; + private const CefMenuCommand MenuScreenshotTweet = (CefMenuCommand)26614; private const string TitleReloadBrowser = "Reload browser"; private const string TitleMuteNotifications = "Mute notifications"; @@ -55,14 +55,14 @@ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser br } if (!string.IsNullOrEmpty(lastHighlightedTweet) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){ - model.AddItem((CefMenuCommand)MenuOpenTweetUrl, "Open tweet in browser"); - model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address"); - model.AddItem((CefMenuCommand)MenuScreenshotTweet, "Screenshot tweet to clipboard"); + model.AddItem(MenuOpenTweetUrl, "Open tweet in browser"); + model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); + model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard"); if (!string.IsNullOrEmpty(lastHighlightedQuotedTweet)){ model.AddSeparator(); - model.AddItem((CefMenuCommand)MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); - model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); + model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser"); + model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); } model.AddSeparator(); @@ -71,16 +71,16 @@ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser br if ((parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){ AddSeparator(model); - IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu((CefMenuCommand)MenuGlobal, Program.BrandName); + IMenuModel globalMenu = model.Count == 0 ? model : model.AddSubMenu(MenuGlobal, Program.BrandName); globalMenu.AddItem(CefMenuCommand.Reload, TitleReloadBrowser); - globalMenu.AddCheckItem((CefMenuCommand)MenuMute, TitleMuteNotifications); - globalMenu.SetChecked((CefMenuCommand)MenuMute, Program.UserConfig.MuteNotifications); + globalMenu.AddCheckItem(MenuMute, TitleMuteNotifications); + globalMenu.SetChecked(MenuMute, Program.UserConfig.MuteNotifications); globalMenu.AddSeparator(); - globalMenu.AddItem((CefMenuCommand)MenuSettings, TitleSettings); - globalMenu.AddItem((CefMenuCommand)MenuPlugins, TitlePlugins); - globalMenu.AddItem((CefMenuCommand)MenuAbout, TitleAboutProgram); + globalMenu.AddItem(MenuSettings, TitleSettings); + globalMenu.AddItem(MenuPlugins, TitlePlugins); + globalMenu.AddItem(MenuAbout, TitleAboutProgram); if (HasDevTools){ globalMenu.AddSeparator(); @@ -96,8 +96,8 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b return true; } - switch((int)commandId){ - case (int)CefMenuCommand.Reload: + switch(commandId){ + case CefMenuCommand.Reload: form.InvokeAsyncSafe(form.ReloadToTweetDeck); return true; diff --git a/Core/Handling/ContextMenuNotification.cs b/Core/Handling/ContextMenuNotification.cs index a28cc789..1afdb0be 100644 --- a/Core/Handling/ContextMenuNotification.cs +++ b/Core/Handling/ContextMenuNotification.cs @@ -4,11 +4,11 @@ namespace TweetDuck.Core.Handling{ class ContextMenuNotification : ContextMenuBase{ - private const int MenuViewDetail = 26600; - private const int MenuSkipTweet = 26601; - private const int MenuFreeze = 26602; - private const int MenuCopyTweetUrl = 26603; - private const int MenuCopyQuotedTweetUrl = 26604; + private const CefMenuCommand MenuViewDetail = (CefMenuCommand)26600; + private const CefMenuCommand MenuSkipTweet = (CefMenuCommand)26601; + private const CefMenuCommand MenuFreeze = (CefMenuCommand)26602; + private const CefMenuCommand MenuCopyTweetUrl = (CefMenuCommand)26603; + private const CefMenuCommand MenuCopyQuotedTweetUrl = (CefMenuCommand)26604; private readonly FormNotificationBase form; private readonly bool enableCustomMenu; @@ -29,17 +29,17 @@ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser br base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model); if (enableCustomMenu){ - model.AddItem((CefMenuCommand)MenuViewDetail, "View detail"); - model.AddItem((CefMenuCommand)MenuSkipTweet, "Skip tweet"); - model.AddCheckItem((CefMenuCommand)MenuFreeze, "Freeze"); - model.SetChecked((CefMenuCommand)MenuFreeze, form.FreezeTimer); - model.AddSeparator(); + model.AddItem(MenuViewDetail, "View detail"); + model.AddItem(MenuSkipTweet, "Skip tweet"); + model.AddCheckItem(MenuFreeze, "Freeze"); + model.SetChecked(MenuFreeze, form.FreezeTimer); if (!string.IsNullOrEmpty(form.CurrentTweetUrl)){ - model.AddItem((CefMenuCommand)MenuCopyTweetUrl, "Copy tweet address"); + model.AddSeparator(); + model.AddItem(MenuCopyTweetUrl, "Copy tweet address"); if (!string.IsNullOrEmpty(form.CurrentQuoteUrl)){ - model.AddItem((CefMenuCommand)MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); + model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address"); } } } @@ -59,7 +59,7 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b return true; } - switch((int)commandId){ + switch(commandId){ case MenuSkipTweet: form.InvokeAsyncSafe(form.FinishCurrentNotification); return true; diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js index 5bd94f84..b5d2f8c8 100644 --- a/Resources/Scripts/code.js +++ b/Resources/Scripts/code.js @@ -376,17 +376,16 @@ // Block: Allow bypassing of t.co and include media previews in context menus. // $(document.body).delegate("a", "contextmenu", function(){ - $TD.setLastRightClickedLink($(this).attr("data-full-url") || ""); - }); - - $(document.body).delegate("a.js-media-image-link", "contextmenu", function(){ let me = $(this)[0]; - if (me.firstElementChild){ - $TD.setLastRightClickedImage(me.firstElementChild.getAttribute("src")); + if (me.classList.contains("js-media-image-link") && highlightedTweetObj){ + let media = (highlightedTweetObj.quotedTweet || highlightedTweetObj).getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id")); + + $TD.setLastRightClickInfo("image", media.large()); + } } else{ - $TD.setLastRightClickedImage(me.style.backgroundImage.replace(/url\(['"]?(.*?)['"]?\)/, "$1")); + $TD.setLastRightClickInfo("link", me.getAttribute("data-full-url")); } });