diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index c72a359a..7ac0ae07 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -9,9 +9,9 @@ - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Text-Grab-Package/Package.appxmanifest b/Text-Grab-Package/Package.appxmanifest index d8206dcf..79891a93 100644 --- a/Text-Grab-Package/Package.appxmanifest +++ b/Text-Grab-Package/Package.appxmanifest @@ -11,7 +11,7 @@ + Version="4.7.0.0" /> Text Grab diff --git a/Text-Grab-Package/Text-Grab-Package.wapproj b/Text-Grab-Package/Text-Grab-Package.wapproj index fa97345d..ccba0d23 100644 --- a/Text-Grab-Package/Text-Grab-Package.wapproj +++ b/Text-Grab-Package/Text-Grab-Package.wapproj @@ -161,5 +161,10 @@ True + + + + + \ No newline at end of file diff --git a/Text-Grab/App.config b/Text-Grab/App.config index f852fa6e..d6361c43 100644 --- a/Text-Grab/App.config +++ b/Text-Grab/App.config @@ -145,6 +145,15 @@ Resize + + True + + + + + + + \ No newline at end of file diff --git a/Text-Grab/Controls/BottomBarSettings.xaml b/Text-Grab/Controls/BottomBarSettings.xaml index 906121bf..113a0121 100644 --- a/Text-Grab/Controls/BottomBarSettings.xaml +++ b/Text-Grab/Controls/BottomBarSettings.xaml @@ -74,7 +74,8 @@ Margin="0,0,8,0" d:Symbol="Diamond24" FontSize="24" - Symbol="{Binding Path=SymbolIcon, Mode=TwoWay}" /> + Symbol="{Binding Path=SymbolIcon, + Mode=TwoWay}" /> @@ -173,7 +174,8 @@ Margin="0,0,8,0" d:Symbol="Diamond24" FontSize="24" - Symbol="{Binding Path=SymbolIcon, Mode=TwoWay}" /> + Symbol="{Binding Path=SymbolIcon, + Mode=TwoWay}" /> diff --git a/Text-Grab/Controls/CollapsibleButton.xaml b/Text-Grab/Controls/CollapsibleButton.xaml index 0f0059b5..a29f0dcc 100644 --- a/Text-Grab/Controls/CollapsibleButton.xaml +++ b/Text-Grab/Controls/CollapsibleButton.xaml @@ -24,7 +24,9 @@ + Symbol="{Binding Path=ButtonSymbol, + Mode=TwoWay, + ElementName=CollapsibleButtonUserControl}" /> + Text="{Binding ElementName=CollapsibleButtonUserControl, + Path=ButtonText, + Mode=TwoWay}" /> @@ -40,7 +44,8 @@ Name="ChangeButtonLayout" Click="ChangeButtonLayout_Click" Header="Change Style" - IsEnabled="{Binding ElementName=CollapsibleButtonUserControl, Path=CanChangeStyle}" /> + IsEnabled="{Binding ElementName=CollapsibleButtonUserControl, + Path=CanChangeStyle}" /> diff --git a/Text-Grab/Models/ButtonInfo.cs b/Text-Grab/Models/ButtonInfo.cs index 3f7cdab2..cb3d6419 100644 --- a/Text-Grab/Models/ButtonInfo.cs +++ b/Text-Grab/Models/ButtonInfo.cs @@ -200,33 +200,9 @@ public ButtonInfo(string buttonText, string symbolText, string background, strin new() { OrderNumber = 1.7, - ButtonText = "Google...", + ButtonText = "Web Search", SymbolText = "", - Command = "GoogleSearchCmd", - SymbolIcon = SymbolRegular.GlobeSearch24 - }, - new() - { - OrderNumber = 1.8, - ButtonText = "Bing...", - SymbolText = "", - Command = "BingSearchCmd", - SymbolIcon = SymbolRegular.GlobeSearch24 - }, - new() - { - OrderNumber = 1.9, - ButtonText = "Duck Duck Go...", - SymbolText = "", - Command = "DuckDuckGoSearchCmd", - SymbolIcon = SymbolRegular.GlobeSearch24 - }, - new() - { - OrderNumber = 1.91, - ButtonText = "Search GitHub...", - SymbolText = "", - Command = "GitHubSearchCmd", + Command = "DefaultWebSearchCmd", SymbolIcon = SymbolRegular.GlobeSearch24 }, new() diff --git a/Text-Grab/Models/LookupItem.cs b/Text-Grab/Models/LookupItem.cs index 3beebb5b..64102818 100644 --- a/Text-Grab/Models/LookupItem.cs +++ b/Text-Grab/Models/LookupItem.cs @@ -1,11 +1,37 @@ -using System; +using Humanizer; +using System; namespace Text_Grab.Models; +public enum LookupItemKind +{ + Simple = 0, + EditWindow = 1, + GrabFrame = 2, + Link = 3, +} + public class LookupItem : IEquatable { - public string shortValue { get; set; } = string.Empty; - public string longValue { get; set; } = string.Empty; + public string ShortValue { get; set; } = string.Empty; + public string LongValue { get; set; } = string.Empty; + + public Wpf.Ui.Controls.SymbolRegular UiSymbol + { + get + { + return Kind switch + { + LookupItemKind.Simple => Wpf.Ui.Controls.SymbolRegular.Copy20, + LookupItemKind.EditWindow => Wpf.Ui.Controls.SymbolRegular.Window24, + LookupItemKind.GrabFrame => Wpf.Ui.Controls.SymbolRegular.PanelBottom20, + LookupItemKind.Link => Wpf.Ui.Controls.SymbolRegular.Link24, + _ => Wpf.Ui.Controls.SymbolRegular.Copy20, + }; + } + } + + public LookupItemKind Kind { get; set; } = LookupItemKind.Simple; public LookupItem() { @@ -14,13 +40,34 @@ public LookupItem() public LookupItem(string sv, string lv) { - shortValue = sv; - longValue = lv; + ShortValue = sv; + LongValue = lv; } - public override string ToString() => $"{shortValue} {longValue}"; + public LookupItem(HistoryInfo historyInfo) + { + ShortValue = historyInfo.CaptureDateTime.Humanize() + Environment.NewLine + historyInfo.CaptureDateTime.ToString("F"); + LongValue = historyInfo.TextContent.Length > 100 ? historyInfo.TextContent[..100].Trim() + "…" : historyInfo.TextContent.Trim(); + + HistoryItem = historyInfo; + + if (string.IsNullOrEmpty(historyInfo.ImagePath)) + Kind = LookupItemKind.EditWindow; + else + Kind = LookupItemKind.GrabFrame; + } + + public HistoryInfo? HistoryItem { get; set; } + + public override string ToString() + { + if (HistoryItem is not null) + return $"{HistoryItem.CaptureDateTime:F} {HistoryItem.TextContent}"; + + return $"{ShortValue} {LongValue}"; + } - public string ToCSVString() => $"{shortValue},{longValue}"; + public string ToCSVString() => $"{ShortValue},{LongValue}"; public bool Equals(LookupItem? other) { @@ -32,4 +79,4 @@ public bool Equals(LookupItem? other) return false; } -} \ No newline at end of file +} diff --git a/Text-Grab/Models/WebSearchUrlModel.cs b/Text-Grab/Models/WebSearchUrlModel.cs new file mode 100644 index 00000000..7053d22f --- /dev/null +++ b/Text-Grab/Models/WebSearchUrlModel.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Text_Grab.Utilities; + +namespace Text_Grab.Models; + +public record WebSearchUrlModel +{ + public string Name { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; + + private WebSearchUrlModel? defaultSearcher; + + public WebSearchUrlModel DefaultSearcher + { + get + { + defaultSearcher ??= GetDefaultSearcher(); + return defaultSearcher; + } + set + { + defaultSearcher = value; + SaveDefaultSearcher(defaultSearcher); + } + } + + public override string ToString() => Name; + + private List webSearchers = []; + + public List WebSearchers + { + get + { + if (webSearchers.Count == 0) + webSearchers = GetWebSearchUrls(); + + return webSearchers; + } + set + { + webSearchers = value; + SaveWebSearchUrls(webSearchers); + } + } + + private WebSearchUrlModel GetDefaultSearcher() + { + string searcherName = AppUtilities.TextGrabSettings.DefaultWebSearch; + if (string.IsNullOrWhiteSpace(searcherName)) + return WebSearchers[0]; + + WebSearchUrlModel? searcher = WebSearchers + .FirstOrDefault(searcher => searcher.Name == searcherName); + + return searcher ?? WebSearchers[0]; + } + + private void SaveDefaultSearcher(WebSearchUrlModel webSearchUrl) + { + AppUtilities.TextGrabSettings.DefaultWebSearch = webSearchUrl.Name; + AppUtilities.TextGrabSettings.Save(); + } + + private static List GetDefaultWebSearchUrls() + { + return + [ + new() { Name = "Google", Url = "https://www.google.com/search?q=" }, + new() { Name = "Bing", Url = "https://www.bing.com/search?q=" }, + new() { Name = "DuckDuckGo", Url = "https://duckduckgo.com/?q=" }, + new() { Name = "Brave", Url = "https://search.brave.com/search?q=" }, + new() { Name = "GitHub Code", Url = "https://github.com/search?type=code&q=" }, + new() { Name = "GitHub Repos", Url = "https://github.com/search?type=repositories&q=" }, + ]; + } + + public static List GetWebSearchUrls() + { + string json = AppUtilities.TextGrabSettings.WebSearchItemsJson; + if (string.IsNullOrWhiteSpace(json)) + return GetDefaultWebSearchUrls(); + List? webSearchUrls = JsonSerializer.Deserialize>(json); + if (webSearchUrls is null || webSearchUrls.Count == 0) + return GetDefaultWebSearchUrls(); + + return webSearchUrls; + } + + public static void SaveWebSearchUrls(List webSearchUrls) + { + string json = JsonSerializer.Serialize(webSearchUrls); + AppUtilities.TextGrabSettings.WebSearchItemsJson = json; + AppUtilities.TextGrabSettings.Save(); + } +} diff --git a/Text-Grab/Pages/GeneralSettings.xaml b/Text-Grab/Pages/GeneralSettings.xaml index ae144ccf..39ade307 100644 --- a/Text-Grab/Pages/GeneralSettings.xaml +++ b/Text-Grab/Pages/GeneralSettings.xaml @@ -6,6 +6,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Text_Grab.Pages" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:models="clr-namespace:Text_Grab.Models" xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml" Title="GeneralSettings" d:DesignHeight="1450" @@ -27,7 +28,7 @@ x:Name="VersionTextblock" VerticalAlignment="Center" Style="{StaticResource TextBodyNormal}" - Text="Version 4.6.0" /> + Text="Version 4.7.0" /> + + + + + + + + + searcherSettings = Singleton.Instance.WebSearchers; + + foreach (WebSearchUrlModel searcher in searcherSettings) + WebSearchersComboBox.Items.Add(searcher); + + WebSearchersComboBox.SelectedItem = Singleton.Instance.DefaultSearcher; + ShowToastCheckBox.IsChecked = DefaultSettings.ShowToast; RunInBackgroundChkBx.IsChecked = DefaultSettings.RunInTheBackground; @@ -356,4 +366,14 @@ private void ShowToastCheckBox_Unchecked(object sender, RoutedEventArgs e) DefaultSettings.ShowToast = false; } + + private void WebSearchersComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (!settingsSet + || sender is not ComboBox comboBox + || comboBox.SelectedItem is not WebSearchUrlModel newDefault) + return; + + Singleton.Instance.DefaultSearcher = newDefault; + } } diff --git a/Text-Grab/Properties/Settings.Designer.cs b/Text-Grab/Properties/Settings.Designer.cs index 95b517f6..d5a18153 100644 --- a/Text-Grab/Properties/Settings.Designer.cs +++ b/Text-Grab/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace Text_Grab.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.12.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -574,5 +574,41 @@ public string GrabFrameScrollBehavior { this["GrabFrameScrollBehavior"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool LookupSearchHistory { + get { + return ((bool)(this["LookupSearchHistory"])); + } + set { + this["LookupSearchHistory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string DefaultWebSearch { + get { + return ((string)(this["DefaultWebSearch"])); + } + set { + this["DefaultWebSearch"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WebSearchItemsJson { + get { + return ((string)(this["WebSearchItemsJson"])); + } + set { + this["WebSearchItemsJson"] = value; + } + } } } diff --git a/Text-Grab/Properties/Settings.settings b/Text-Grab/Properties/Settings.settings index fc262f6b..a9a2887f 100644 --- a/Text-Grab/Properties/Settings.settings +++ b/Text-Grab/Properties/Settings.settings @@ -140,5 +140,14 @@ Resize + + True + + + + + + + \ No newline at end of file diff --git a/Text-Grab/Services/HistoryService.cs b/Text-Grab/Services/HistoryService.cs index 5271b12d..fafcd154 100644 --- a/Text-Grab/Services/HistoryService.cs +++ b/Text-Grab/Services/HistoryService.cs @@ -23,9 +23,9 @@ public class HistoryService private static readonly int maxHistoryTextOnly = 100; private static readonly int maxHistoryWithImages = 10; - private List HistoryTextOnly = new(); - private List HistoryWithImage = new(); - private DispatcherTimer saveTimer = new(); + private List HistoryTextOnly = []; + private List HistoryWithImage = []; + private readonly DispatcherTimer saveTimer = new(); private readonly Settings DefaultSettings = AppUtilities.TextGrabSettings; #endregion Fields @@ -119,7 +119,7 @@ public async Task LoadHistories() public async Task PopulateMenuItemWithRecentGrabs(MenuItem recentGrabsMenuItem) { List grabsHistory = GetRecentGrabs(); - grabsHistory = grabsHistory.OrderByDescending(x => x.CaptureDateTime).ToList(); + grabsHistory = [.. grabsHistory.OrderByDescending(x => x.CaptureDateTime)]; recentGrabsMenuItem.Items.Clear(); @@ -244,6 +244,23 @@ public void WriteHistory() WriteHistoryFiles(HistoryWithImage, nameof(HistoryWithImage), maxHistoryWithImages); } } + + public void RemoveTextHistoryItem(HistoryInfo historyItem) + { + HistoryTextOnly.Remove(historyItem); + + saveTimer.Stop(); + saveTimer.Start(); + } + + public void RemoveImageHistoryItem(HistoryInfo historyItem) + { + HistoryWithImage.Remove(historyItem); + + saveTimer.Stop(); + saveTimer.Start(); + } + #endregion Public Methods #region Private Methods @@ -252,14 +269,14 @@ private static async Task> LoadHistory(string fileName) { string rawText = await FileUtilities.GetTextFileAsync($"{fileName}.json", FileStorageKind.WithHistory); - if (string.IsNullOrWhiteSpace(rawText)) return new List(); + if (string.IsNullOrWhiteSpace(rawText)) return []; List? tempHistory = JsonSerializer.Deserialize>(rawText); if (tempHistory is List jsonList && jsonList.Count > 0) return tempHistory; - return new List(); + return []; } private static void WriteHistoryFiles(List history, string fileName, int maxNumberToSave) @@ -310,5 +327,6 @@ private void SaveTimer_Tick(object? sender, EventArgs e) WriteHistory(); CachedBitmap = null; } + #endregion Private Methods } diff --git a/Text-Grab/Styles/ButtonStyles.xaml b/Text-Grab/Styles/ButtonStyles.xaml index 184df4d8..4edc2705 100644 --- a/Text-Grab/Styles/ButtonStyles.xaml +++ b/Text-Grab/Styles/ButtonStyles.xaml @@ -218,7 +218,8 @@ x:Name="PART_Popup" AllowsTransparency="true" Focusable="false" - IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" + IsOpen="{Binding IsSubmenuOpen, + RelativeSource={RelativeSource TemplatedParent}}" Placement="Bottom" PlacementTarget="{Binding ElementName=templateRoot}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}"> @@ -237,9 +238,12 @@ VerticalAlignment="Top"> + Width="{Binding ActualWidth, + ElementName=SubMenuBorder}" + Height="{Binding ActualHeight, + ElementName=SubMenuBorder}" + Fill="{Binding Background, + ElementName=SubMenuBorder}" /> @@ -469,9 +474,12 @@ VerticalAlignment="Top"> + Width="{Binding ActualWidth, + ElementName=SubMenuBorder}" + Height="{Binding ActualHeight, + ElementName=SubMenuBorder}" + Fill="{Binding Background, + ElementName=SubMenuBorder}" /> + Visibility="{Binding HeadersVisibility, + ConverterParameter={x:Static DataGridHeadersVisibility.Row}, + Converter={x:Static DataGrid.HeadersVisibilityConverter}, + RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" /> @@ -155,4 +161,4 @@ - \ No newline at end of file + diff --git a/Text-Grab/Styles/TextBoxStyles.xaml b/Text-Grab/Styles/TextBoxStyles.xaml index c2555038..16bd0c4b 100644 --- a/Text-Grab/Styles/TextBoxStyles.xaml +++ b/Text-Grab/Styles/TextBoxStyles.xaml @@ -169,7 +169,8 @@ x:Name="Arrow" HorizontalAlignment="Center" VerticalAlignment="Center" - Data="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"> + Data="{Binding Content, + RelativeSource={RelativeSource TemplatedParent}}"> @@ -359,4 +360,4 @@ - \ No newline at end of file + diff --git a/Text-Grab/Text-Grab.csproj b/Text-Grab/Text-Grab.csproj index 757c06f9..6ccd2424 100644 --- a/Text-Grab/Text-Grab.csproj +++ b/Text-Grab/Text-Grab.csproj @@ -68,11 +68,11 @@ - + - + diff --git a/Text-Grab/Utilities/CustomBottomBarUtilities.cs b/Text-Grab/Utilities/CustomBottomBarUtilities.cs index 90293561..840993f5 100644 --- a/Text-Grab/Utilities/CustomBottomBarUtilities.cs +++ b/Text-Grab/Utilities/CustomBottomBarUtilities.cs @@ -20,14 +20,15 @@ public static List GetCustomBottomBarItemsSetting() if (string.IsNullOrWhiteSpace(json)) return ButtonInfo.DefaultButtonList; - List? customBottomBarItems = new(); + List? customBottomBarItems = []; customBottomBarItems = JsonSerializer.Deserialize>(json); if (customBottomBarItems is null || customBottomBarItems.Count == 0) return ButtonInfo.DefaultButtonList; - // check to see if the first element is using the default symbol, Diamond24, which is unused by any button + // check to see if the first element is using the default symbol of Diamond24 + // which is unused by any button if (customBottomBarItems.First().SymbolIcon == SymbolRegular.Diamond24) { // Migrate to the new SymbolRegular instead of the old symbols. @@ -37,14 +38,12 @@ public static List GetCustomBottomBarItemsSetting() buttonInfo.SymbolIcon = buttonDictionary[buttonInfo.ButtonText]; } - return customBottomBarItems; } - // a method to save a list of collapsible buttons to the settings as json public static void SaveCustomBottomBarItemsSetting(List bottomBarButtons) { - List customButtons = new(); + List customButtons = []; foreach (CollapsibleButton collapsible in bottomBarButtons) customButtons.Add(new(collapsible)); @@ -54,23 +53,20 @@ public static void SaveCustomBottomBarItemsSetting(List botto public static void SaveCustomBottomBarItemsSetting(List bottomBarButtons) { - // serialize the list of custom bottom bar items to json string json = JsonSerializer.Serialize(bottomBarButtons); - - // save the json string to the settings AppUtilities.TextGrabSettings.BottomButtonsJson = json; - - // save the settings AppUtilities.TextGrabSettings.Save(); } public static List GetBottomBarButtons(EditTextWindow editTextWindow) { - List bottomBarButtons = new(); - Dictionary _localRoutedCommands = new(); + List bottomBarButtons = []; + Dictionary _localRoutedCommands = []; List methods = GetMethods(editTextWindow); Dictionary routedCommands = EditTextWindow.GetRoutedCommands(); + int index = 1; + foreach (ButtonInfo buttonItem in GetCustomBottomBarItemsSetting()) { CollapsibleButton button = new() @@ -78,7 +74,7 @@ public static List GetBottomBarButtons(EditTextWindow editTex ButtonText = buttonItem.ButtonText, IsSymbol = buttonItem.IsSymbol, CustomButton = buttonItem, - ToolTip = buttonItem.ButtonText, + ToolTip = $"{buttonItem.ButtonText} (ctrl + {index})", ButtonSymbol = buttonItem.SymbolIcon }; @@ -97,18 +93,17 @@ public static List GetBottomBarButtons(EditTextWindow editTex button.Command = routedCommand; bottomBarButtons.Add(button); + index++; } return bottomBarButtons; } - // a method which returns a list of all methods in this class private static List GetMethods(object obj) { - return obj.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).ToList(); + return [.. obj.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)]; } - // using the above method match a method name to a string parameter private static MethodInfo? GetMethodInfoForName(string methodName, List methods) { foreach (MethodInfo method in methods) @@ -118,7 +113,6 @@ private static List GetMethods(object obj) return null; } - // a method to match a command name to a string parameter private static RoutedCommand? GetCommandBinding(string commandName, Dictionary routedCommands) { foreach (string commandKey in routedCommands.Keys) diff --git a/Text-Grab/Utilities/StringMethods.cs b/Text-Grab/Utilities/StringMethods.cs index 11c162c5..d9a7b39e 100644 --- a/Text-Grab/Utilities/StringMethods.cs +++ b/Text-Grab/Utilities/StringMethods.cs @@ -122,6 +122,15 @@ public static (int, int) CursorWordBoundaries(this string input, int cursorPosit if (cursorPosition < 0) cursorPosition = 0; + try + { + char check = input[cursorPosition]; + } + catch (IndexOutOfRangeException) + { + return (cursorPosition, 0); + } + // Check if the cursor is at a space if (char.IsWhiteSpace(input[cursorPosition])) cursorPosition = FindNearestLetterIndex(input, cursorPosition); diff --git a/Text-Grab/Views/EditTextWindow.xaml b/Text-Grab/Views/EditTextWindow.xaml index cf95f750..f741192a 100644 --- a/Text-Grab/Views/EditTextWindow.xaml +++ b/Text-Grab/Views/EditTextWindow.xaml @@ -129,20 +129,12 @@ Executed="MakeQrCodeExecuted" /> + Command="{x:Static local:EditTextWindow.WebSearchCmd}" + Executed="WebSearchExecuted" /> - - + Command="{x:Static local:EditTextWindow.DefaultWebSearchCmd}" + Executed="DefaultWebSearchExecuted" /> @@ -269,21 +261,10 @@ Click="FindAndReplaceMenuItem_Click" Header="Find and Replace" /> - - - + x:Name="DefaultWebSearch" + Command="{x:Static local:EditTextWindow.DefaultWebSearchCmd}" + Header="Default Web Search" /> + GetRoutedCommands() {nameof(InsertSelectionOnEveryLineCmd), InsertSelectionOnEveryLineCmd}, {nameof(OcrPasteCommand), OcrPasteCommand}, {nameof(MakeQrCodeCmd), MakeQrCodeCmd}, - {nameof(GoogleSearchCmd), GoogleSearchCmd}, - {nameof(BingSearchCmd), BingSearchCmd}, - {nameof(DuckDuckGoSearchCmd), DuckDuckGoSearchCmd}, - {nameof(GitHubSearchCmd), GitHubSearchCmd}, + {nameof(WebSearchCmd), WebSearchCmd}, + {nameof(DefaultWebSearchCmd), DefaultWebSearchCmd}, }; } @@ -178,7 +174,7 @@ public async Task OcrAllImagesInFolder(string folderPath, OcrDirectoryOptions op { files = Directory.GetFiles(folderPath, "*.*", searchOption); } - catch (System.Exception ex) + catch (Exception ex) { PassedTextControl.AppendText($"Failed to read directory: {ex.Message}{Environment.NewLine}"); } @@ -244,7 +240,7 @@ public async Task OcrAllImagesInFolder(string folderPath, OcrDirectoryOptions op stopwatch.Start(); Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; - List ocrFileResults = new(); + List ocrFileResults = []; foreach (string path in imageFiles) { AsyncOcrFileResult ocrFileResult = new(path); @@ -551,7 +547,7 @@ private void CanLaunchUriExecute(object sender, CanExecuteRoutedEventArgs e) private void CanOcrPasteExecute(object sender, CanExecuteRoutedEventArgs e) { - _IsAccessingClipboard = true; + IsAccessingClipboard = true; DataPackageView? dataPackageView = null; try @@ -565,7 +561,7 @@ private void CanOcrPasteExecute(object sender, CanExecuteRoutedEventArgs e) } finally { - _IsAccessingClipboard = false; + IsAccessingClipboard = false; } if (dataPackageView is null) @@ -620,17 +616,17 @@ private void CheckRightToLeftLanguage() private async void Clipboard_ContentChanged(object? sender, object e) { - if (ClipboardWatcherMenuItem.IsChecked is false || _IsAccessingClipboard) + if (ClipboardWatcherMenuItem.IsChecked is false || IsAccessingClipboard) return; - _IsAccessingClipboard = true; + IsAccessingClipboard = true; DataPackageView? dataPackageView = null; try { dataPackageView = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent(); } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with Windows.ApplicationModel.DataTransfer.Clipboard.GetContent(). Exception Message: {ex.Message}"); } @@ -644,13 +640,13 @@ private async void Clipboard_ContentChanged(object? sender, object e) text += Environment.NewLine; System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { AddCopiedTextToTextBox(text); })); } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with dataPackageView.GetTextAsync(). Exception Message: {ex.Message}"); } }; - _IsAccessingClipboard = false; + IsAccessingClipboard = false; } private void CloseMenuItem_Click(object sender, RoutedEventArgs e) @@ -899,7 +895,7 @@ private void HideBottomBarMenuItem_Click(object sender, RoutedEventArgs e) private void InsertSelectionOnEveryLine(object? sender = null, ExecutedRoutedEventArgs? e = null) { - string[] splitString = PassedTextControl.Text.Split(new string[] { System.Environment.NewLine }, StringSplitOptions.None); + string[] splitString = PassedTextControl.Text.Split([Environment.NewLine], StringSplitOptions.None); string selectionText = PassedTextControl.SelectedText; int initialSelectionStart = PassedTextControl.SelectionStart; int selectionPositionInLine = PassedTextControl.SelectionStart; @@ -995,12 +991,35 @@ private async void GitHubSearchExecuted(object sender, ExecutedRoutedEventArgs e _ = await Windows.System.Launcher.LaunchUriAsync(new Uri(string.Format($"https://github.com/search?q={searchStringUrlSafe}"))); } - private void keyedCtrlF(object sender, ExecutedRoutedEventArgs e) + private async void WebSearchExecuted(object sender, ExecutedRoutedEventArgs e) + { + string possibleSearch = PassedTextControl.SelectedText; + string searchStringUrlSafe = WebUtility.UrlEncode(possibleSearch); + + if (e.Parameter is not WebSearchUrlModel webSearcher) + return; + + Uri searchUri = new($"{webSearcher.Url}{searchStringUrlSafe}"); + _ = await Windows.System.Launcher.LaunchUriAsync(searchUri); + } + + private async void DefaultWebSearchExecuted(object sender, ExecutedRoutedEventArgs e) + { + string possibleSearch = PassedTextControl.SelectedText; + string searchStringUrlSafe = WebUtility.UrlEncode(possibleSearch); + + WebSearchUrlModel searcher = Singleton.Instance.DefaultSearcher; + + Uri searchUri = new($"{searcher.Url}{searchStringUrlSafe}"); + _ = await Windows.System.Launcher.LaunchUriAsync(searchUri); + } + + private void KeyedCtrlF(object sender, ExecutedRoutedEventArgs e) { WindowUtilities.LaunchFullScreenGrab(PassedTextControl); } - private void keyedCtrlG(object sender, ExecutedRoutedEventArgs e) + private void KeyedCtrlG(object sender, ExecutedRoutedEventArgs e) { CheckForGrabFrameOrLaunch(); } @@ -1152,7 +1171,7 @@ private async void LoadLanguageMenuItems(MenuItem captureMenuItem) private void LoadRecentTextHistory() { List grabsHistories = Singleton.Instance.GetEditWindows(); - grabsHistories = grabsHistories.OrderByDescending(x => x.CaptureDateTime).ToList(); + grabsHistories = [.. grabsHistories.OrderByDescending(x => x.CaptureDateTime)]; OpenRecentMenuItem.Items.Clear(); @@ -1435,21 +1454,21 @@ private void PassedTextControl_TextChanged(object sender, TextChangedEventArgs e private async void PasteExecuted(object sender, ExecutedRoutedEventArgs? e = null) { - _IsAccessingClipboard = true; + IsAccessingClipboard = true; DataPackageView? dataPackageView = null; try { dataPackageView = Windows.ApplicationModel.DataTransfer.Clipboard.GetContent(); } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with Windows.ApplicationModel.DataTransfer.Clipboard.GetContent(). Exception Message: {ex.Message}"); } if (dataPackageView is null) { - _IsAccessingClipboard = false; + IsAccessingClipboard = false; return; } @@ -1460,7 +1479,7 @@ private async void PasteExecuted(object sender, ExecutedRoutedEventArgs? e = nul string textFromClipboard = await dataPackageView.GetTextAsync(); System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { AddCopiedTextToTextBox(textFromClipboard); })); } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with dataPackageView.GetTextAsync(). Exception Message: {ex.Message}"); } @@ -1476,7 +1495,7 @@ private async void PasteExecuted(object sender, ExecutedRoutedEventArgs? e = nul System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { AddCopiedTextToTextBox(text); })); } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with dataPackageView.GetBitmapAsync(). Exception Message: {ex.Message}"); } @@ -1501,13 +1520,13 @@ private async void PasteExecuted(object sender, ExecutedRoutedEventArgs? e = nul System.Windows.Application.Current.Dispatcher.Invoke(new Action(() => { AddCopiedTextToTextBox(text); })); } } - catch (System.Exception ex) + catch (Exception ex) { Debug.WriteLine($"error with dataPackageView.GetStorageItemsAsync(). Exception Message: {ex.Message}"); } } - _IsAccessingClipboard = false; + IsAccessingClipboard = false; if (e is not null) e.Handled = true; @@ -1777,7 +1796,7 @@ private void SetFontFromSettings() if (DefaultSettings.IsFontItalic) PassedTextControl.FontStyle = FontStyles.Italic; - TextDecorationCollection tdc = new(); + TextDecorationCollection tdc = []; if (DefaultSettings.IsFontUnderline) tdc.Add(TextDecorations.Underline); if (DefaultSettings.IsFontStrikeout) tdc.Add(TextDecorations.Strikethrough); PassedTextControl.TextDecorations = tdc; @@ -1810,11 +1829,11 @@ private void SetupRoutedCommands() { RoutedCommand newFullscreenGrab = new(); _ = newFullscreenGrab.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control)); - _ = CommandBindings.Add(new CommandBinding(newFullscreenGrab, keyedCtrlF)); + _ = CommandBindings.Add(new CommandBinding(newFullscreenGrab, KeyedCtrlF)); RoutedCommand newGrabFrame = new(); _ = newGrabFrame.InputGestures.Add(new KeyGesture(Key.G, ModifierKeys.Control)); - _ = CommandBindings.Add(new CommandBinding(newGrabFrame, keyedCtrlG)); + _ = CommandBindings.Add(new CommandBinding(newGrabFrame, KeyedCtrlG)); RoutedCommand selectLineCommand = new(); _ = selectLineCommand.InputGestures.Add(new KeyGesture(Key.L, ModifierKeys.Control)); @@ -1883,6 +1902,20 @@ private void SetupRoutedCommands() RoutedCommand duplicateLine = new(); _ = duplicateLine.InputGestures.Add(new KeyGesture(Key.D, ModifierKeys.Control)); _ = CommandBindings.Add(new CommandBinding(duplicateLine, DuplicateSelectedLine)); + + List searchers = Singleton.Instance.WebSearchers; + + foreach (WebSearchUrlModel searcher in searchers) + { + MenuItem searchItem = new() + { + Header = $"Search with {searcher.Name}...", + Command = WebSearchCmd, + CommandParameter = searcher, + }; + + WebSearchCollection.Items.Add(searchItem); + } } private void SingleLineCmdCanExecute(object sender, CanExecuteRoutedEventArgs e) diff --git a/Text-Grab/Views/FullscreenGrab.xaml b/Text-Grab/Views/FullscreenGrab.xaml index 63fa6daa..35894d0f 100644 --- a/Text-Grab/Views/FullscreenGrab.xaml +++ b/Text-Grab/Views/FullscreenGrab.xaml @@ -161,7 +161,9 @@ Height="34" Margin="4,0,2,0" Click="FreezeMenuItem_Click" - IsChecked="{Binding IsChecked, ElementName=FreezeMenuItem, Mode=TwoWay}" + IsChecked="{Binding IsChecked, + ElementName=FreezeMenuItem, + Mode=TwoWay}" Style="{StaticResource ToggleSymbolButton}" ToolTip="(F) Freeze what is on screens"> @@ -191,7 +193,9 @@ Margin="0" d:IsChecked="True" Click="SingleLineMenuItem_Click" - IsChecked="{Binding IsChecked, ElementName=SingleLineMenuItem, Mode=TwoWay}" + IsChecked="{Binding IsChecked, + ElementName=SingleLineMenuItem, + Mode=TwoWay}" Style="{StaticResource ToggleSymbolButton}" ToolTip="(S) Make result a single line"> @@ -203,7 +207,9 @@ Margin="0" d:IsChecked="True" Click="TableToggleButton_Click" - IsChecked="{Binding IsChecked, ElementName=TableMenuItem, Mode=TwoWay}" + IsChecked="{Binding IsChecked, + ElementName=TableMenuItem, + Mode=TwoWay}" Style="{StaticResource ToggleSymbolButton}" ToolTip="(T) OCR text as a table"> @@ -215,13 +221,67 @@ Margin="0" d:IsChecked="True" Click="NewGrabFrameMenuItem_Click" - IsChecked="{Binding IsChecked, ElementName=NewGrabFrameMenuItem, Mode=TwoWay}" + IsChecked="{Binding IsChecked, + ElementName=NewGrabFrameMenuItem, + Mode=TwoWay}" Style="{StaticResource ToggleSymbolButton}" ToolTip="(G) Place a Grab Frame"> + + + + + + + + + + + + + + diff --git a/Text-Grab/Views/FullscreenGrab.xaml.cs b/Text-Grab/Views/FullscreenGrab.xaml.cs index 7087ae44..53be209b 100644 --- a/Text-Grab/Views/FullscreenGrab.xaml.cs +++ b/Text-Grab/Views/FullscreenGrab.xaml.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.Net; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -39,7 +40,7 @@ public partial class FullscreenGrab : Window private double xShiftDelta; private double yShiftDelta; private HistoryInfo? historyInfo; - private bool usingTesseract; + private readonly bool usingTesseract; private static readonly Settings DefaultSettings = AppUtilities.TextGrabSettings; #endregion Fields @@ -182,8 +183,23 @@ internal void KeyPressed(Key key, bool? isActive = null) case Key.D8: case Key.D9: int numberPressed = (int)key - 34; // D1 casts to 35, D2 to 36, etc. - int numberOfLanguages = LanguagesComboBox.Items.Count; + if (KeyboardExtensions.IsCtrlDown()) + { + if (NextStepDropDownButton.Flyout is not ContextMenu flyoutMenu + || !flyoutMenu.HasItems + || numberPressed - 1 >= flyoutMenu.Items.Count + || flyoutMenu.Items[numberPressed - 1] is not MenuItem selectedItem) + { + return; + } + + selectedItem.IsChecked = !selectedItem.IsChecked; + CheckIfAnyPostActionsSelcted(); + return; + } + + int numberOfLanguages = LanguagesComboBox.Items.Count; if (numberPressed <= numberOfLanguages && numberPressed - 1 >= 0 && numberPressed - 1 != LanguagesComboBox.SelectedIndex @@ -195,6 +211,25 @@ internal void KeyPressed(Key key, bool? isActive = null) } } + private void CheckIfAnyPostActionsSelcted() + { + if (NextStepDropDownButton.Flyout is not ContextMenu flyoutMenu || !flyoutMenu.HasItems) + return; + + foreach (object anyItem in flyoutMenu.Items) + { + if (anyItem is MenuItem item && item.IsChecked) + { + if (FindResource("DarkTeal") is SolidColorBrush tealButtonStyle) + NextStepDropDownButton.Background = tealButtonStyle; + return; + } + } + + if (FindResource("ControlFillColorDefaultBrush") is SolidColorBrush SymbolButtonStyle) + NextStepDropDownButton.Background = SymbolButtonStyle; + } + private static bool CheckIfCheckingOrUnchecking(object? sender) { bool isActive = false; @@ -637,26 +672,59 @@ private async void RegionClickCanvas_MouseUp(object sender, MouseButtonEventArgs }; } - if (!string.IsNullOrWhiteSpace(TextFromOCR)) + if (string.IsNullOrWhiteSpace(TextFromOCR)) { - if (SendToEditTextToggleButton.IsChecked is true && destinationTextBox is null) - { - EditTextWindow etw = WindowUtilities.OpenOrActivateWindow(); - destinationTextBox = etw.PassedTextControl; - } + BackgroundBrush.Opacity = .2; + TopButtonsStackPanel.Visibility = Visibility.Visible; + return; + } + + if (GuidFixMenuItem.IsChecked is true) + TextFromOCR = TextFromOCR.CorrectCommonGuidErrors(); + + if (TrimEachLineMenuItem.IsChecked is true) + { + string workingString = TextFromOCR; + string[] stringSplit = workingString.Split(Environment.NewLine); + + string finalString = ""; + foreach (string line in stringSplit) + if (!string.IsNullOrWhiteSpace(line)) + finalString += line.Trim() + Environment.NewLine; - OutputUtilities.HandleTextFromOcr( - TextFromOCR, - isSingleLine, - isTable, - destinationTextBox); - WindowUtilities.CloseAllFullscreenGrabs(); + TextFromOCR = finalString; } - else + + if (RemoveDuplicatesMenuItem.IsChecked is true) + TextFromOCR = TextFromOCR.RemoveDuplicateLines(); + + if (WebSearchPostCapture.IsChecked is true) { - BackgroundBrush.Opacity = .2; - TopButtonsStackPanel.Visibility = Visibility.Visible; + string searchStringUrlSafe = WebUtility.UrlEncode(TextFromOCR); + + WebSearchUrlModel searcher = Singleton.Instance.DefaultSearcher; + + Uri searchUri = new($"{searcher.Url}{searchStringUrlSafe}"); + _ = await Windows.System.Launcher.LaunchUriAsync(searchUri); } + + if (SendToEditTextToggleButton.IsChecked is true + && destinationTextBox is null + && WebSearchPostCapture.IsChecked is false) + { + EditTextWindow etw = WindowUtilities.OpenOrActivateWindow(); + destinationTextBox = etw.PassedTextControl; + } + + OutputUtilities.HandleTextFromOcr( + TextFromOCR, + isSingleLine, + isTable, + destinationTextBox); + WindowUtilities.CloseAllFullscreenGrabs(); + + if (InsertPostCapture.IsChecked is true && !DefaultSettings.TryInsert) + await WindowUtilities.TryInsertString(TextFromOCR); } private void SendToEditTextToggleButton_Click(object sender, RoutedEventArgs e) @@ -805,5 +873,10 @@ private void TableToggleButton_Click(object? sender = null, RoutedEventArgs? e = WindowUtilities.FullscreenKeyDown(Key.T, isActive); SelectSingleToggleButton(sender); } + + private void PostActionMenuItem_Click(object sender, RoutedEventArgs e) + { + CheckIfAnyPostActionsSelcted(); + } #endregion Methods } diff --git a/Text-Grab/Views/QuickSimpleLookup.xaml b/Text-Grab/Views/QuickSimpleLookup.xaml index c81435af..2c11ca67 100644 --- a/Text-Grab/Views/QuickSimpleLookup.xaml +++ b/Text-Grab/Views/QuickSimpleLookup.xaml @@ -11,6 +11,7 @@ Width="800" Height="400" Closed="FluentWindow_Closed" + Closing="FluentWindow_Closing" Loaded="Window_Loaded" PreviewKeyDown="QuickSimpleLookup_PreviewKeyDown" Topmost="True" @@ -23,7 +24,7 @@ @@ -151,6 +152,11 @@ x:Name="ParseCSVFileMenuItem" Click="ParseCSVFileMenuItem_Click" Header="Add Rows from CSV File..." /> + + + + + + + + + + + + + + + + - + - + @@ -225,7 +255,7 @@ - + @@ -233,11 +263,31 @@ + Text="{Binding LongValue}" /> + + + + + + + + + + + + + + diff --git a/Text-Grab/Views/QuickSimpleLookup.xaml.cs b/Text-Grab/Views/QuickSimpleLookup.xaml.cs index 0b85f879..c072593b 100644 --- a/Text-Grab/Views/QuickSimpleLookup.xaml.cs +++ b/Text-Grab/Views/QuickSimpleLookup.xaml.cs @@ -13,24 +13,24 @@ using System.Windows.Media; using Text_Grab.Models; using Text_Grab.Properties; +using Text_Grab.Services; using Text_Grab.Utilities; namespace Text_Grab.Views; -/// -/// Interaction logic for QuickSimpleLookup.xaml -/// public partial class QuickSimpleLookup : Wpf.Ui.Controls.FluentWindow { #region Fields public TextBox? DestinationTextBox; - private string cacheFilename = "QuickSimpleLookupCache.csv"; + private readonly string cacheFilename = "QuickSimpleLookupCache.csv"; private bool isPuttingValueIn = false; private LookupItem? lastSelection; private int rowCount = 0; private string valueUnderEdit = string.Empty; + private LookupItem? itemUnderEdit; private static readonly Settings DefaultSettings = AppUtilities.TextGrabSettings; + private List lookupItems = []; #endregion Fields @@ -48,7 +48,7 @@ public QuickSimpleLookup() public bool IsEditingDataGrid { get; set; } = false; public bool IsFromETW { get; set; } = false; - public List ItemsDictionary { get; set; } = new(); + public List ItemsDictionary { get; set; } = []; #endregion Properties @@ -56,20 +56,20 @@ public QuickSimpleLookup() private static LookupItem ParseStringToLookupItem(char splitChar, string row) { - List cells = row.Split(splitChar).ToList(); + List cells = [.. row.Split(splitChar)]; LookupItem newRow = new(); - if (cells.FirstOrDefault() is String firstCell) - newRow.shortValue = firstCell; + if (cells.FirstOrDefault() is string firstCell) + newRow.ShortValue = firstCell; - newRow.longValue = ""; + newRow.LongValue = ""; if (cells.Count > 1 && cells[1] is not null) - newRow.longValue = String.Join(" ", cells.Skip(1).ToArray()); + newRow.LongValue = string.Join(" ", cells.Skip(1).ToArray()); return newRow; } private static IEnumerable ParseStringToRows(string clipboardContent, bool isCSV = false) { - List rows = clipboardContent.Split(Environment.NewLine).ToList(); + List rows = [.. clipboardContent.Split(Environment.NewLine)]; char splitChar = isCSV ? ',' : '\t'; @@ -98,7 +98,7 @@ private void AddToLookUpResults(char splitChar, string text) MainDataGrid.ItemsSource = null; ItemsDictionary.Add(newItem); - MainDataGrid.ItemsSource = ItemsDictionary; + lookupItems.Add(newItem); UpdateRowCount(); MainDataGrid.ScrollIntoView(ItemsDictionary.LastOrDefault()); @@ -186,9 +186,10 @@ private void FluentWindow_Closed(object sender, EventArgs e) private List GetMainDataGridSelection() { - if (MainDataGrid.SelectedItems is not List selectedItems || selectedItems.Count == 0) + if (MainDataGrid.SelectedItems is not List selectedItems + || selectedItems.Count == 0) { - selectedItems = new List(); + selectedItems = []; if (MainDataGrid.SelectedItem is not LookupItem selectedLookupItem) return selectedItems; @@ -225,6 +226,7 @@ private void GoToEndOfMainDataGrid() private void MainDataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e) { IsEditingDataGrid = true; + itemUnderEdit = e.Row.Item as LookupItem; } private void MainDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) @@ -232,7 +234,27 @@ private void MainDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEv IsEditingDataGrid = false; if (e.EditAction == DataGridEditAction.Cancel) + { + itemUnderEdit = null; return; + } + + LookupItem? editedRow = e.Row.Item as LookupItem; + int prevIndex = -1; + + if (itemUnderEdit is not null) + { + prevIndex = lookupItems.IndexOf(itemUnderEdit); + lookupItems.Remove(itemUnderEdit); + } + + if (editedRow is not null) + { + if (prevIndex > -1) + lookupItems.Insert(prevIndex, editedRow); + else + lookupItems.Add(editedRow); + } DependencyObject child = VisualTreeHelper.GetChild(e.EditingElement, 0); if (child is TextBox editedBox @@ -281,12 +303,13 @@ private void ParseBTN_Click(object sender, RoutedEventArgs e) private async void ParseCSVFileMenuItem_Click(object sender, RoutedEventArgs e) { // Create OpenFileDialog - Microsoft.Win32.OpenFileDialog dlg = new(); - - // Set filter for file extension and default file extension - dlg.DefaultExt = ".csv"; - dlg.Filter = "Comma Separated Values File (.csv)|*.csv"; - dlg.CheckFileExists = true; + OpenFileDialog dlg = new() + { + // Set filter for file extension and default file extension + DefaultExt = ".csv", + Filter = "Comma Separated Values File (.csv)|*.csv", + CheckFileExists = true + }; bool? result = dlg.ShowDialog(); @@ -295,7 +318,7 @@ private async void ParseCSVFileMenuItem_Click(object sender, RoutedEventArgs e) string csvToOpenPath = dlg.FileName; - await ReadCsvFileIntoQuickSimpleLookup(csvToOpenPath); + await LoadDataGridContent(csvToOpenPath); SaveBTN.Visibility = Visibility.Visible; } @@ -308,17 +331,18 @@ private void PasteToggleButton_Checked(object sender, RoutedEventArgs e) private async void PickSaveLocation_Click(object sender, RoutedEventArgs e) { - SaveFileDialog dlg = new(); - - dlg.AddExtension = true; - dlg.DefaultExt = ".csv"; - dlg.InitialDirectory = "C:\\"; - dlg.FileName = "QuickSimpleLookupDataFile.csv"; - dlg.OverwritePrompt = false; + SaveFileDialog dlg = new() + { + AddExtension = true, + DefaultExt = ".csv", + InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + FileName = "QuickSimpleLookupDataFile.csv", + OverwritePrompt = false + }; if (!string.IsNullOrEmpty(DefaultSettings.LookupFileLocation)) { - dlg.InitialDirectory = DefaultSettings.LookupFileLocation; + dlg.InitialDirectory = Path.GetDirectoryName(DefaultSettings.LookupFileLocation); dlg.FileName = Path.GetFileName(DefaultSettings.LookupFileLocation); } @@ -334,7 +358,7 @@ private async void PickSaveLocation_Click(object sender, RoutedEventArgs e) { // clear and load the new file ItemsDictionary.Clear(); - await ReadCsvFileIntoQuickSimpleLookup(dlg.FileName); + await LoadDataGridContent(dlg.FileName); } else await WriteDataToCSV(); @@ -342,16 +366,28 @@ private async void PickSaveLocation_Click(object sender, RoutedEventArgs e) private void PopulateSampleData() { - LookupItem sampleItem1 = new("This is the key", "This is the value you want to copy quickly"); + LookupItem sampleItem1 = new("This is the key", "This is the value you want to copy quickly") + { + Kind = LookupItemKind.Simple + }; ItemsDictionary.Add(sampleItem1); - LookupItem sampleItem2 = new("Import data", "From a copied Excel table, or import from a CSV File"); + LookupItem sampleItem2 = new("Import data", "From a copied Excel table, or import from a CSV File") + { + Kind = LookupItemKind.Simple + }; ItemsDictionary.Add(sampleItem2); - LookupItem sampleItem3 = new("You can change save location", "Putting the data store location in OneDrive it will sync across devices"); + LookupItem sampleItem3 = new("You can change save location", "Putting the data store location in OneDrive it will sync across devices") + { + Kind = LookupItemKind.Simple + }; ItemsDictionary.Add(sampleItem3); - LookupItem sampleItem4 = new("Delete these initial rows", "and add your own manually if you like."); + LookupItem sampleItem4 = new("Delete these initial rows", "and add your own manually if you like.") + { + Kind = LookupItemKind.Simple + }; ItemsDictionary.Add(sampleItem4); MainDataGrid.ItemsSource = null; @@ -362,11 +398,17 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null) { if (MainDataGrid.ItemsSource is not List lookUpList || lookUpList.FirstOrDefault() is not LookupItem firstLookupItem) + { + EditTextWindow etw = new(SearchBox.Text, false); + etw.Show(); + this.Close(); + WindowUtilities.ShouldShutDown(); return; + } isPuttingValueIn = true; - List selectedLookupItems = new(); + List selectedLookupItems = []; foreach (object item in MainDataGrid.SelectedItems) if (item is LookupItem selectedLookupItem) @@ -375,10 +417,10 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null) if (selectedLookupItems.Count == 0) selectedLookupItems.Add(firstLookupItem); + bool openedHistoryItemOrLink = false; StringBuilder stringBuilder = new(); - if (keysDown is null) - keysDown = KeyboardExtensions.GetKeyboardModifiersDown(); + keysDown ??= KeyboardExtensions.GetKeyboardModifiersDown(); switch (keysDown) { @@ -397,16 +439,16 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null) if (selectedLookupItems.FirstOrDefault() is not LookupItem lookupItem) return; - if (Uri.TryCreate(lookupItem.longValue, UriKind.Absolute, out Uri? uri)) + if (Uri.TryCreate(lookupItem.LongValue, UriKind.Absolute, out Uri? uri)) { - Process.Start(new ProcessStartInfo(lookupItem.longValue) { UseShellExecute = true }); + Process.Start(new ProcessStartInfo(lookupItem.LongValue) { UseShellExecute = true }); this.Close(); return; } break; case KeyboardModifiersDown.Ctrl: foreach (LookupItem lItem in selectedLookupItems) - stringBuilder.AppendLine(lItem.shortValue); + stringBuilder.AppendLine(lItem.ShortValue); break; case KeyboardModifiersDown.Shift: foreach (LookupItem lItem in selectedLookupItems) @@ -414,10 +456,42 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null) break; default: foreach (LookupItem lItem in selectedLookupItems) - stringBuilder.AppendLine(lItem.longValue); + { + switch (lItem.Kind) + { + case LookupItemKind.EditWindow when lItem.HistoryItem is not null: + { + EditTextWindow editTextWindow = new(lItem.HistoryItem); + editTextWindow.Show(); + openedHistoryItemOrLink = true; + break; + } + case LookupItemKind.GrabFrame when lItem.HistoryItem is not null: + { + GrabFrame gf = new(lItem.HistoryItem); + gf.Show(); + openedHistoryItemOrLink = true; + break; + } + case LookupItemKind.Link: + Process.Start(new ProcessStartInfo(lItem.LongValue) { UseShellExecute = true }); + openedHistoryItemOrLink = true; + break; + default: + stringBuilder.AppendLine(lItem.LongValue); + break; + } + } break; } + if (openedHistoryItemOrLink) + { + this.Close(); + WindowUtilities.ShouldShutDown(); + return; + } + if (string.IsNullOrEmpty(stringBuilder.ToString())) return; @@ -543,7 +617,7 @@ private async void QuickSimpleLookup_PreviewKeyDown(object sender, KeyEventArgs } } - private async Task ReadCsvFileIntoQuickSimpleLookup(string csvToOpenPath) + private async Task LoadDataGridContent(string csvToOpenPath) { string contentToParse = string.Empty; @@ -555,9 +629,17 @@ private async Task ReadCsvFileIntoQuickSimpleLookup(string csvToOpenPath) if (string.IsNullOrWhiteSpace(contentToParse)) PopulateSampleData(); + MainDataGrid.ItemsSource = null; + ItemsDictionary.AddRange(ParseStringToRows(contentToParse, true)); + lookupItems = [.. ItemsDictionary]; + + if (DefaultSettings.LookupSearchHistory) + { + AddHistoryItemsToItemsDictionary(); + SearchHistory.IsChecked = true; + } - MainDataGrid.ItemsSource = null; MainDataGrid.ItemsSource = ItemsDictionary; UpdateRowCount(); @@ -578,8 +660,19 @@ private void RowDeleted() if (item is LookupItem selectedLookupItem) { filteredLookupList.Remove(selectedLookupItem); + lookupItems.Remove(selectedLookupItem); ItemsDictionary.Remove(selectedLookupItem); - SaveBTN.Visibility = Visibility.Visible; + + if (selectedLookupItem.HistoryItem is null) + { + SaveBTN.Visibility = Visibility.Visible; + continue; + } + + if (selectedLookupItem.Kind is LookupItemKind.EditWindow) + Singleton.Instance.RemoveTextHistoryItem(selectedLookupItem.HistoryItem); + else if (selectedLookupItem.Kind is LookupItemKind.GrabFrame) + Singleton.Instance.RemoveImageHistoryItem(selectedLookupItem.HistoryItem); } } @@ -647,10 +740,10 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e) else MainDataGrid.CanUserAddRows = false; - List searchArray = SearchBox.Text.ToLower().Split().ToList(); + List searchArray = [.. SearchBox.Text.ToLower().Split()]; searchArray.Sort(); - List filteredList = new(); + List filteredList = []; foreach (LookupItem lItem in ItemsDictionary) { @@ -691,7 +784,7 @@ private void UpdateRowCount() private async void Window_Loaded(object sender, RoutedEventArgs e) { - await ReadCsvFileIntoQuickSimpleLookup(DefaultSettings.LookupFileLocation); + await LoadDataGridContent(DefaultSettings.LookupFileLocation); if (DefaultSettings.TryInsert && !IsFromETW) PasteToggleButton.IsChecked = true; @@ -703,6 +796,26 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) Activate(); SearchBox.Focus(); } + + private void AddHistoryItemsToItemsDictionary() + { + List textHistoryItems = Singleton.Instance.GetEditWindows(); + + foreach (HistoryInfo historyItem in textHistoryItems) + { + LookupItem newItem = new(historyItem); + ItemsDictionary.Add(newItem); + } + + List grabFrameHistoryItems = Singleton.Instance.GetRecentGrabs(); + + foreach (HistoryInfo historyItem in grabFrameHistoryItems) + { + LookupItem newItem = new(historyItem); + ItemsDictionary.Add(newItem); + } + } + private async Task WriteDataToCSV() { if (!string.IsNullOrWhiteSpace(SearchBox.Text)) @@ -710,10 +823,7 @@ private async Task WriteDataToCSV() StringBuilder csvContents = new(); - if (MainDataGrid.ItemsSource is not List itemsToSave) - return; - - foreach (LookupItem lookupItem in itemsToSave) + foreach (LookupItem lookupItem in lookupItems) csvContents.AppendLine(lookupItem.ToCSVString()); try @@ -730,5 +840,80 @@ private async Task WriteDataToCSV() System.Windows.Forms.MessageBox.Show($"Failed to save csv file. {ex.Message}"); } } + + private void FluentWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + // Can't do this here because it will close the app before the text can be inserted + // WindowUtilities.ShouldShutDown(); + } + + private async void SearchHistory_Click(object sender, RoutedEventArgs e) + { + DefaultSettings.LookupSearchHistory = SearchHistory.IsChecked is true; + + lookupItems.Clear(); + ItemsDictionary.Clear(); + MainDataGrid.ItemsSource = null; + + await LoadDataGridContent(DefaultSettings.LookupFileLocation); + } + + private void DeleteItem_Click(object sender, RoutedEventArgs e) + { + if (MainDataGrid.SelectedItem is not LookupItem lookupItem) + return; + + RowDeleted(); + } + + private void OpenInETWMenuItem_Click(object sender, RoutedEventArgs e) + { + if (MainDataGrid.SelectedItem is not LookupItem lookupItem) + return; + + switch (lookupItem.Kind) + { + case LookupItemKind.Simple: + StringBuilder sb = new(); + sb.Append(lookupItem.ShortValue); + sb.Append(Environment.NewLine); + sb.AppendLine(lookupItem.LongValue); + EditTextWindow etw = new(sb.ToString(), false); + etw.Show(); + break; + case LookupItemKind.EditWindow: + if (lookupItem.HistoryItem is not null) + { + EditTextWindow editTextWindow = new(lookupItem.HistoryItem); + editTextWindow.Show(); + return; + } + + EditTextWindow etw2 = new(lookupItem.LongValue, false); + etw2.Show(); + break; + case LookupItemKind.GrabFrame: + if (lookupItem.HistoryItem is not null) + { + EditTextWindow editTextWindow = new(lookupItem.HistoryItem); + editTextWindow.Show(); + return; + } + + EditTextWindow etw3 = new(lookupItem.LongValue, false); + etw3.Show(); + break; + case LookupItemKind.Link: + StringBuilder sb2 = new(); + sb2.Append(lookupItem.ShortValue); + sb2.Append(Environment.NewLine); + sb2.AppendLine(lookupItem.LongValue); + EditTextWindow etw4 = new(sb2.ToString(), false); + etw4.Show(); + break; + default: + break; + } + } #endregion Methods }