Skip to content

Commit 668f8b1

Browse files
authored
QSL regex commands links (#510)
* by default starting short value with > runs as powershell * Search with first letters of each word in ShortValue of Lookup item * Enable searching by RegEx * enable regex when doing ctrl + r
1 parent 2c8a74f commit 668f8b1

File tree

5 files changed

+208
-11
lines changed

5 files changed

+208
-11
lines changed

Text-Grab/Controls/RegExIcon.xaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<UserControl
2+
x:Class="Text_Grab.Controls.RegExIcon"
3+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6+
xmlns:local="clr-namespace:Text_Grab.Controls"
7+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
8+
d:DesignHeight="200"
9+
d:DesignWidth="222"
10+
mc:Ignorable="d">
11+
<Viewbox>
12+
<Path Data="M148 97L117.5 149.5L87.5 128L125 82L75 71.5L86 38.5L135.5 57.5L125 0H171.5L161 57.5L210.5 38.5L221.5 71.5L171.5 82L209 128L179 149.5L148 97Z M0 129 L75 129 L 75 204 L0 204 z" Fill="{Binding IconColor}" />
13+
</Viewbox>
14+
</UserControl>

Text-Grab/Controls/RegExIcon.xaml.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Windows;
2+
using System.Windows.Controls;
3+
using System.Windows.Media;
4+
5+
namespace Text_Grab.Controls;
6+
7+
public partial class RegExIcon : UserControl
8+
{
9+
public SolidColorBrush IconColor
10+
{
11+
get { return (SolidColorBrush)GetValue(IconColorProperty); }
12+
set { SetValue(IconColorProperty, value); }
13+
}
14+
15+
public static readonly DependencyProperty IconColorProperty =
16+
DependencyProperty.Register("IconColor", typeof(SolidColorBrush), typeof(RegExIcon), new PropertyMetadata(null));
17+
18+
public RegExIcon()
19+
{
20+
DataContext = this;
21+
InitializeComponent();
22+
}
23+
}

Text-Grab/Models/LookupItem.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Humanizer;
22
using System;
3+
using System.Linq;
34

45
namespace Text_Grab.Models;
56

@@ -9,6 +10,8 @@ public enum LookupItemKind
910
EditWindow = 1,
1011
GrabFrame = 2,
1112
Link = 3,
13+
Command = 4,
14+
Dynamic = 5,
1215
}
1316

1417
public class LookupItem : IEquatable<LookupItem>
@@ -26,6 +29,8 @@ public Wpf.Ui.Controls.SymbolRegular UiSymbol
2629
LookupItemKind.EditWindow => Wpf.Ui.Controls.SymbolRegular.Window24,
2730
LookupItemKind.GrabFrame => Wpf.Ui.Controls.SymbolRegular.PanelBottom20,
2831
LookupItemKind.Link => Wpf.Ui.Controls.SymbolRegular.Link24,
32+
LookupItemKind.Command => Wpf.Ui.Controls.SymbolRegular.WindowConsole20,
33+
LookupItemKind.Dynamic => Wpf.Ui.Controls.SymbolRegular.Flash24,
2934
_ => Wpf.Ui.Controls.SymbolRegular.Copy20,
3035
};
3136
}
@@ -38,6 +43,8 @@ public LookupItem()
3843

3944
}
4045

46+
public string FirstLettersString => string.Join("", ShortValue.Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(s => s[0])).ToLower();
47+
4148
public LookupItem(string sv, string lv)
4249
{
4350
ShortValue = sv;

Text-Grab/Views/QuickSimpleLookup.xaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,22 @@
6666
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
6767
IsHitTestVisible="False"
6868
Opacity="0.5" />
69+
<ToggleButton
70+
x:Name="RegExToggleButton"
71+
Grid.Column="0"
72+
Height="26"
73+
Margin="0,0,5,0"
74+
Padding="7,4"
75+
HorizontalAlignment="Right"
76+
VerticalAlignment="Center"
77+
BorderThickness="1"
78+
Checked="RegExToggleButton_Checked"
79+
Style="{StaticResource ToggleSymbolButton}"
80+
Unchecked="RegExToggleButton_Checked">
81+
<Viewbox Width="12" Height="12">
82+
<controls:RegExIcon IconColor="{DynamicResource TextFillColorPrimaryBrush}" />
83+
</Viewbox>
84+
</ToggleButton>
6985
<StackPanel Grid.Column="1" Orientation="Horizontal">
7086
<controls:CollapsibleButton
7187
x:Name="AddItemBtn"

Text-Grab/Views/QuickSimpleLookup.xaml.cs

Lines changed: 148 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.Win32;
1+
using CliWrap.Buffered;
2+
using CliWrap;
3+
using Microsoft.Win32;
24
using System;
35
using System.Collections.Generic;
46
using System.Diagnostics;
@@ -15,6 +17,7 @@
1517
using Text_Grab.Properties;
1618
using Text_Grab.Services;
1719
using Text_Grab.Utilities;
20+
using System.Text.RegularExpressions;
1821

1922
namespace Text_Grab.Views;
2023

@@ -56,6 +59,22 @@ public QuickSimpleLookup()
5659

5760
private static LookupItem ParseStringToLookupItem(char splitChar, string row)
5861
{
62+
LookupItemKind kind = LookupItemKind.Simple;
63+
if (row.StartsWith('>'))
64+
{
65+
kind = LookupItemKind.Command;
66+
}
67+
else if (row.StartsWith("http"))
68+
kind = LookupItemKind.Link;
69+
else if (row.StartsWith("🔗"))
70+
{
71+
kind = LookupItemKind.Link;
72+
}
73+
else if (row.StartsWith('⚡'))
74+
{
75+
kind = LookupItemKind.Dynamic;
76+
}
77+
5978
List<string> cells = [.. row.Split(splitChar)];
6079
LookupItem newRow = new();
6180
if (cells.FirstOrDefault() is string firstCell)
@@ -64,6 +83,8 @@ private static LookupItem ParseStringToLookupItem(char splitChar, string row)
6483
newRow.LongValue = "";
6584
if (cells.Count > 1 && cells[1] is not null)
6685
newRow.LongValue = string.Join(" ", cells.Skip(1).ToArray());
86+
87+
newRow.Kind = kind;
6788
return newRow;
6889
}
6990

@@ -477,6 +498,11 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null)
477498
Process.Start(new ProcessStartInfo(lItem.LongValue) { UseShellExecute = true });
478499
openedHistoryItemOrLink = true;
479500
break;
501+
case LookupItemKind.Command:
502+
openedHistoryItemOrLink = await RunCli(lItem.LongValue);
503+
break;
504+
case LookupItemKind.Dynamic:
505+
break;
480506
default:
481507
stringBuilder.AppendLine(lItem.LongValue);
482508
break;
@@ -534,6 +560,48 @@ private async void PutValueIntoClipboard(KeyboardModifiersDown? keysDown = null)
534560
WindowUtilities.ShouldShutDown();
535561
}
536562

563+
/// <summary>
564+
///
565+
/// </summary>
566+
/// <param name="longValue"></param>
567+
/// <returns>returns if output is empty or whitespace</returns>
568+
private async Task<bool> RunCli(string longValue)
569+
{
570+
string[] commands = longValue.Split(' ', StringSplitOptions.RemoveEmptyEntries);
571+
572+
if (commands.Length == 0)
573+
return true;
574+
575+
BufferedCommandResult result = await Cli.Wrap("powershell")
576+
.WithArguments(commands)
577+
.WithValidation(CommandResultValidation.None)
578+
.ExecuteBufferedAsync(Encoding.UTF8);
579+
580+
string outputText = result.StandardOutput.Trim();
581+
string errorText = result.StandardError.Trim();
582+
583+
string shortOutput = "Output";
584+
if (string.IsNullOrWhiteSpace(outputText))
585+
{
586+
if (string.IsNullOrWhiteSpace(errorText))
587+
return true;
588+
589+
shortOutput = "ERROR!";
590+
outputText = errorText;
591+
}
592+
593+
LookupItem newItem = new(shortOutput, outputText)
594+
{
595+
Kind = LookupItemKind.Simple
596+
};
597+
598+
MainDataGrid.ItemsSource = null;
599+
List<LookupItem> newItems = [newItem];
600+
MainDataGrid.ItemsSource = newItems;
601+
602+
return false;
603+
}
604+
537605
private async void QuickSimpleLookup_PreviewKeyDown(object sender, KeyEventArgs e)
538606
{
539607
switch (e.Key)
@@ -606,6 +674,13 @@ private async void QuickSimpleLookup_PreviewKeyDown(object sender, KeyEventArgs
606674
e.Handled = true;
607675
}
608676
break;
677+
case Key.R:
678+
if (KeyboardExtensions.IsCtrlDown())
679+
{
680+
RegExToggleButton.IsChecked = !RegExToggleButton.IsChecked;
681+
e.Handled = true;
682+
}
683+
break;
609684
case Key.End:
610685
GoToEndOfMainDataGrid();
611686
break;
@@ -690,12 +765,17 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
690765
if (sender is not TextBox searchingBox || !IsLoaded)
691766
return;
692767

693-
if (string.IsNullOrEmpty(searchingBox.Text))
768+
await ReSearch(searchingBox.Text);
769+
}
770+
771+
private async Task ReSearch(string searchString)
772+
{
773+
if (string.IsNullOrEmpty(searchString))
694774
SearchLabel.Visibility = Visibility.Visible;
695775
else
696776
SearchLabel.Visibility = Visibility.Collapsed;
697777

698-
if (searchingBox.Text.Contains('\t'))
778+
if (searchString.Contains('\t'))
699779
{
700780
// a tab has been entered and this will be a new entry
701781
AddItemBtn.Visibility = Visibility.Visible;
@@ -707,7 +787,7 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
707787

708788
MainDataGrid.ItemsSource = null;
709789

710-
if (string.IsNullOrEmpty(searchingBox.Text))
790+
if (string.IsNullOrEmpty(searchString))
711791
{
712792
MainDataGrid.ItemsSource = ItemsDictionary;
713793
MainDataGrid.CanUserAddRows = true;
@@ -719,9 +799,13 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
719799
if (row is null)
720800
{
721801
MainDataGrid.UpdateLayout();
722-
MainDataGrid.ScrollIntoView(MainDataGrid.Items[lastSelectionInt]);
723-
await Task.Delay(lastSelectionInt > maxMsDelay ? maxMsDelay : lastSelectionInt);
724-
row = (DataGridRow)MainDataGrid.ItemContainerGenerator.ContainerFromIndex(lastSelectionInt);
802+
803+
if (lastSelectionInt > -1)
804+
{
805+
MainDataGrid.ScrollIntoView(MainDataGrid.Items[lastSelectionInt]);
806+
await Task.Delay(lastSelectionInt > maxMsDelay ? maxMsDelay : lastSelectionInt);
807+
row = (DataGridRow)MainDataGrid.ItemContainerGenerator.ContainerFromIndex(lastSelectionInt);
808+
}
725809
}
726810

727811
if (row is not null)
@@ -740,7 +824,55 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
740824
else
741825
MainDataGrid.CanUserAddRows = false;
742826

743-
List<string> searchArray = [.. SearchBox.Text.ToLower().Split()];
827+
if (RegExToggleButton.IsChecked is true)
828+
RegexSearch(searchString);
829+
else
830+
StandardSearch(searchString);
831+
832+
UpdateRowCount();
833+
}
834+
835+
private void RegexSearch(string searchString)
836+
{
837+
Regex searchRegex;
838+
839+
try
840+
{
841+
searchRegex = new Regex(searchString, RegexOptions.IgnoreCase);
842+
}
843+
catch
844+
{
845+
RegExToggleButton.BorderBrush = Brushes.Red;
846+
RegExToggleButton.ToolTip = "Invalid Regular Expression";
847+
return;
848+
}
849+
850+
RegExToggleButton.BorderBrush = Brushes.Transparent;
851+
RegExToggleButton.ToolTip = "Searh using Regular Expression Syntax";
852+
853+
List<LookupItem> filteredList = [];
854+
855+
foreach (LookupItem lItem in ItemsDictionary)
856+
{
857+
string lItemAsString = lItem.ToString().ToLower();
858+
859+
if (searchRegex.IsMatch(lItemAsString)
860+
|| lItem.FirstLettersString.Contains(SearchBox.Text.ToLower(), StringComparison.CurrentCultureIgnoreCase))
861+
filteredList.Add(lItem);
862+
}
863+
864+
MainDataGrid.ItemsSource = filteredList;
865+
866+
if (MainDataGrid.Items.Count > 0)
867+
MainDataGrid.SelectedIndex = 0;
868+
}
869+
870+
private void StandardSearch(string searchString)
871+
{
872+
RegExToggleButton.BorderBrush = Brushes.Transparent;
873+
RegExToggleButton.ToolTip = "Searh using Regular Expression Syntax";
874+
875+
List<string> searchArray = [.. searchString.ToLower().Split()];
744876
searchArray.Sort();
745877

746878
List<LookupItem> filteredList = [];
@@ -756,16 +888,15 @@ private async void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
756888
matchAllSearchWords = false;
757889
}
758890

759-
if (matchAllSearchWords)
891+
if (matchAllSearchWords
892+
|| lItem.FirstLettersString.Contains(SearchBox.Text.ToLower(), StringComparison.CurrentCultureIgnoreCase))
760893
filteredList.Add(lItem);
761894
}
762895

763896
MainDataGrid.ItemsSource = filteredList;
764897

765898
if (MainDataGrid.Items.Count > 0)
766899
MainDataGrid.SelectedIndex = 0;
767-
768-
UpdateRowCount();
769900
}
770901

771902
private void TextGrabSettingsMenuItem_Click(object sender, RoutedEventArgs e)
@@ -915,5 +1046,11 @@ private void OpenInETWMenuItem_Click(object sender, RoutedEventArgs e)
9151046
break;
9161047
}
9171048
}
1049+
1050+
private async void RegExToggleButton_Checked(object sender, RoutedEventArgs e)
1051+
{
1052+
await ReSearch(SearchBox.Text);
1053+
}
1054+
9181055
#endregion Methods
9191056
}

0 commit comments

Comments
 (0)