using System; using System.Text.RegularExpressions; using System.Windows.Forms; using CefSharp; using TweetDuck.Core.Controls; using TweetDuck.Core.Utils; using System.Linq; using TweetDuck.Configuration; using TweetDuck.Core.Bridge; using TweetDuck.Core.Management; using TweetDuck.Core.Notification; using TweetDuck.Core.Other; using TweetDuck.Core.Other.Analytics; using TweetDuck.Resources; using TweetLib.Core.Utils; namespace TweetDuck.Core.Handling{ abstract class ContextMenuBase : IContextMenuHandler{ protected static UserConfig Config => Program.Config.User; private static TwitterUtils.ImageQuality ImageQuality => Config.TwitterImageQuality; private const CefMenuCommand MenuOpenLinkUrl = (CefMenuCommand)26500; private const CefMenuCommand MenuCopyLinkUrl = (CefMenuCommand)26501; private const CefMenuCommand MenuCopyUsername = (CefMenuCommand)26502; private const CefMenuCommand MenuViewImage = (CefMenuCommand)26503; private const CefMenuCommand MenuOpenMediaUrl = (CefMenuCommand)26504; private const CefMenuCommand MenuCopyMediaUrl = (CefMenuCommand)26505; private const CefMenuCommand MenuSaveMedia = (CefMenuCommand)26506; private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507; private const CefMenuCommand MenuSearchInBrowser = (CefMenuCommand)26508; private const CefMenuCommand MenuReadApplyROT13 = (CefMenuCommand)26509; private const CefMenuCommand MenuOpenDevTools = (CefMenuCommand)26599; protected ContextInfo.ContextData Context { get; private set; } private readonly AnalyticsFile.IProvider analytics; protected ContextMenuBase(AnalyticsFile.IProvider analytics){ this.analytics = analytics; } public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){ if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){ Context = TweetDeckBridge.ContextInfo.Reset(); } else{ Context = TweetDeckBridge.ContextInfo.Create(parameters); } if (parameters.TypeFlags.HasFlag(ContextMenuType.Selection) && !parameters.TypeFlags.HasFlag(ContextMenuType.Editable)){ model.AddItem(MenuSearchInBrowser, "Search in browser"); model.AddSeparator(); model.AddItem(MenuReadApplyROT13, "Apply ROT13"); model.AddSeparator(); } string TextOpen(string name) => "Open "+name+" in browser"; string TextCopy(string name) => "Copy "+name+" address"; string TextSave(string name) => "Save "+name+" as..."; if (Context.Types.HasFlag(ContextInfo.ContextType.Link) && !Context.UnsafeLinkUrl.EndsWith("tweetdeck.twitter.com/#", StringComparison.Ordinal)){ if (TwitterUtils.RegexAccount.IsMatch(Context.UnsafeLinkUrl)){ model.AddItem(MenuOpenLinkUrl, TextOpen("account")); model.AddItem(MenuCopyLinkUrl, TextCopy("account")); model.AddItem(MenuCopyUsername, "Copy account username"); } else{ model.AddItem(MenuOpenLinkUrl, TextOpen("link")); model.AddItem(MenuCopyLinkUrl, TextCopy("link")); } model.AddSeparator(); } if (Context.Types.HasFlag(ContextInfo.ContextType.Video)){ model.AddItem(MenuOpenMediaUrl, TextOpen("video")); model.AddItem(MenuCopyMediaUrl, TextCopy("video")); model.AddItem(MenuSaveMedia, TextSave("video")); model.AddSeparator(); } else if (Context.Types.HasFlag(ContextInfo.ContextType.Image) && Context.MediaUrl != TweetNotification.AppLogo.Url){ model.AddItem(MenuViewImage, "View image in photo viewer"); model.AddItem(MenuOpenMediaUrl, TextOpen("image")); model.AddItem(MenuCopyMediaUrl, TextCopy("image")); model.AddItem(MenuSaveMedia, TextSave("image")); if (Context.Chirp.Images.Length > 1){ model.AddItem(MenuSaveTweetImages, TextSave("all images")); } model.AddSeparator(); } } public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags){ Control control = browserControl.AsControl(); switch(commandId){ case MenuOpenLinkUrl: OpenBrowser(control, Context.LinkUrl); break; case MenuCopyLinkUrl: SetClipboardText(control, Context.UnsafeLinkUrl); break; case MenuCopyUsername: { string url = Context.UnsafeLinkUrl; Match match = TwitterUtils.RegexAccount.Match(url); SetClipboardText(control, match.Success ? match.Groups[1].Value : url); control.InvokeAsyncSafe(analytics.AnalyticsFile.CopiedUsernames.Trigger); break; } case MenuOpenMediaUrl: OpenBrowser(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); break; case MenuCopyMediaUrl: SetClipboardText(control, TwitterUtils.GetMediaLink(Context.MediaUrl, ImageQuality)); break; case MenuViewImage: { string url = Context.MediaUrl; control.InvokeAsyncSafe(() => { TwitterUtils.ViewImage(url, ImageQuality); analytics.AnalyticsFile.ViewedImages.Trigger(); }); break; } case MenuSaveMedia: { bool isVideo = Context.Types.HasFlag(ContextInfo.ContextType.Video); string url = Context.MediaUrl; string username = Context.Chirp.Authors.LastOrDefault(); control.InvokeAsyncSafe(() => { if (isVideo){ TwitterUtils.DownloadVideo(url, username); analytics.AnalyticsFile.DownloadedVideos.Trigger(); } else{ TwitterUtils.DownloadImage(url, username, ImageQuality); analytics.AnalyticsFile.DownloadedImages.Trigger(); } }); break; } case MenuSaveTweetImages: { string[] urls = Context.Chirp.Images; string username = Context.Chirp.Authors.LastOrDefault(); control.InvokeAsyncSafe(() => { TwitterUtils.DownloadImages(urls, username, ImageQuality); analytics.AnalyticsFile.DownloadedImages.Trigger(); }); break; } case MenuReadApplyROT13: string selection = parameters.SelectionText; control.InvokeAsyncSafe(() => FormMessage.Information("ROT13", StringUtils.ConvertRot13(selection), FormMessage.OK)); control.InvokeAsyncSafe(analytics.AnalyticsFile.UsedROT13.Trigger); return true; case MenuSearchInBrowser: string query = parameters.SelectionText; control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalSearch(query)); DeselectAll(frame); break; case MenuOpenDevTools: browserControl.ShowDevTools(); break; } return false; } public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){ Context = TweetDeckBridge.ContextInfo.Reset(); } public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){ return false; } protected static void DeselectAll(IFrame frame){ ScriptLoader.ExecuteScript(frame, "window.getSelection().removeAllRanges()", "gen:deselect"); } protected static void OpenBrowser(Control control, string url){ control.InvokeAsyncSafe(() => BrowserUtils.OpenExternalBrowser(url)); } protected static void SetClipboardText(Control control, string text){ control.InvokeAsyncSafe(() => WindowsUtils.SetClipboard(text, TextDataFormat.UnicodeText)); } protected static void InsertSelectionSearchItem(IMenuModel model, CefMenuCommand insertCommand, string insertLabel){ model.InsertItemAt(model.GetIndexOf(MenuSearchInBrowser)+1, insertCommand, insertLabel); } protected static void AddDebugMenuItems(IMenuModel model){ if (BrowserUtils.HasDevTools){ AddSeparator(model); model.AddItem(MenuOpenDevTools, "Open dev tools"); } } protected static void RemoveSeparatorIfLast(IMenuModel model){ if (model.Count > 0 && model.GetTypeAt(model.Count-1) == MenuItemType.Separator){ model.RemoveAt(model.Count-1); } } protected static void AddSeparator(IMenuModel model){ if (model.Count > 0 && model.GetTypeAt(model.Count-1) != MenuItemType.Separator){ // do not add separators if there is nothing to separate model.AddSeparator(); } } } }