diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.Designer.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.Designer.cs
index 3fb865fc53..6a2e973a88 100644
--- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.Designer.cs
+++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.Designer.cs
@@ -106,6 +106,7 @@ private void InitializeComponent()
this.toolStripSeparator15 = new System.Windows.Forms.ToolStripSeparator();
this.zoomSpectrumContextMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator27 = new System.Windows.Forms.ToolStripSeparator();
+ this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
((System.ComponentModel.ISupportInitialize)(this.splitPeptideList)).BeginInit();
this.splitPeptideList.Panel1.SuspendLayout();
this.splitPeptideList.Panel2.SuspendLayout();
@@ -279,6 +280,7 @@ private void InitializeComponent()
resources.ApplyResources(this.textPeptide, "textPeptide");
this.textPeptide.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.textPeptide.Name = "textPeptide";
+ this.toolTip1.SetToolTip(this.textPeptide, resources.GetString("textPeptide.ToolTip"));
this.textPeptide.TextChanged += new System.EventHandler(this.textPeptide_TextChanged);
this.textPeptide.KeyDown += new System.Windows.Forms.KeyEventHandler(this.PeptideTextBox_KeyDown);
//
@@ -817,5 +819,6 @@ private void InitializeComponent()
private System.Windows.Forms.ToolStripButton btnCopy;
private System.Windows.Forms.ToolStripButton btnSave;
private System.Windows.Forms.ToolStripButton btnPrint;
+ private System.Windows.Forms.ToolTip toolTip1;
}
}
diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs
index e87b710064..d16296f626 100644
--- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs
+++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.cs
@@ -1228,21 +1228,7 @@ private TextSequence GetCategoryValueTextSequence(int index, Graphics graphics)
var propertyName = _peptides.comboFilterCategoryDict
.FirstOrDefault(x => x.Value == selectedCategory).Key;
- var propertyValue = ViewLibraryPepInfoList.GetStringValue(propertyName, pepInfo);
-
- // Shorten precursor m/z values to be uniform and match the tool tip
- if (selectedCategory.Equals(Resources.PeptideTipProvider_RenderTip_Precursor_m_z))
- {
- propertyValue = FormatPrecursorMz(double.TryParse(propertyValue, out var mz) ? mz : 0);
- }
- else if(selectedCategory.Equals(Resources.PeptideTipProvider_RenderTip_CCS))
- {
- propertyValue = FormatCCS(double.Parse(propertyValue));
- }
- else if(selectedCategory.Equals(Resources.PeptideTipProvider_RenderTip_Ion_Mobility))
- {
- propertyValue = FormatIonMobility(double.Parse(propertyValue), pepInfo.IonMobilityUnits);
- }
+ var propertyValue = ViewLibraryPepInfoList.GetFormattedPropertyValue(propertyName, pepInfo);
categoryText = CreateTextSequence(propertyValue, false);
}
else
@@ -1854,17 +1840,17 @@ public ViewLibrarySettings(bool associateProteins)
}*/
}
- private static string FormatPrecursorMz(double precursorMz)
+ internal static string FormatPrecursorMz(double precursorMz)
{
return string.Format(@"{0:F04}", precursorMz);
}
- private static string FormatIonMobility(double mobility, string units)
+ internal static string FormatIonMobility(double mobility, string units)
{
return string.Format(@"{0:F04} {1}", mobility, units);
}
- private static string FormatCCS(double CCS)
+ internal static string FormatCCS(double CCS)
{
return string.Format(@"{0:F04}", CCS);
}
diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.resx b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.resx
index 3da3f7e7ef..b9904f5d74 100644
--- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.resx
+++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryDlg.resx
@@ -639,6 +639,12 @@
2
+
+ 436, 17
+
+
+ Filters beginning with * search within descriptions, e.g. *PEET will match PEETID and AAPEETB
+
textPeptide
@@ -1665,6 +1671,12 @@
System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ toolTip1
+
+
+ System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
ViewLibraryDlg
diff --git a/pwiz_tools/Skyline/SettingsUI/ViewLibraryPepInfoList.cs b/pwiz_tools/Skyline/SettingsUI/ViewLibraryPepInfoList.cs
index a7562dafa3..33a536e2e7 100644
--- a/pwiz_tools/Skyline/SettingsUI/ViewLibraryPepInfoList.cs
+++ b/pwiz_tools/Skyline/SettingsUI/ViewLibraryPepInfoList.cs
@@ -135,6 +135,47 @@ private List FindValidCategories(List categories)
return categories.Where(category => _allEntries.Any(entry => !GetStringValue(category, entry).Equals(string.Empty))).ToList();
}
+ ///
+ /// Find the string value of a property for a ViewLibraryPepInfo, formatted as in user display
+ ///
+ internal static string GetFormattedPropertyValue(string propertyName, ViewLibraryPepInfo pepInfo)
+ {
+ var property = typeof(ViewLibraryPepInfo).GetProperty(propertyName);
+ if (property is null)
+ {
+ return string.Empty;
+ }
+
+ var value = property.GetValue(pepInfo);
+ if (value is null)
+ {
+ return string.Empty;
+ }
+
+ string propertyValue;
+ var dbl = value as double? ?? 0;
+
+ // Shorten precursor m/z values to be uniform and match the tool tip
+ if (propertyName.Equals(PRECURSOR_MZ))
+ {
+ propertyValue = ViewLibraryDlg.FormatPrecursorMz(dbl);
+ }
+ else if (propertyName.Equals(CCS))
+ {
+ propertyValue = ViewLibraryDlg.FormatCCS(dbl);
+ }
+ else if (propertyName.Equals(ION_MOBILITY))
+ {
+ propertyValue = ViewLibraryDlg.FormatIonMobility(dbl, pepInfo.IonMobilityUnits);
+ }
+ else
+ {
+ propertyValue = value.ToString();
+ }
+
+ return propertyValue;
+ }
+
///
/// Find the string value of a property for a ViewLibraryPepInfo
///
@@ -258,6 +299,29 @@ private List PrefixSearchByProperty(string filterText)
}
+ ///
+ /// Find entries which contain the filter text in the given property
+ ///
+ private List ContainsSearchByProperty(string filterText)
+ {
+ if (string.IsNullOrEmpty(filterText))
+ {
+ return new List();
+ }
+ var orderedList = _listCache.GetOrCreate(_selectedFilterCategory); // List of indexes of items that have values for property of interest
+ IEnumerable matches;
+ if (_accessionNumberTypes.Contains(_selectedFilterCategory))
+ {
+ matches = orderedList.Where(item => _allEntries[item].OtherKeysDict[_selectedFilterCategory]
+ .IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0);
+ }
+ else
+ {
+ matches = orderedList.Where(item => GetFormattedPropertyValue(_selectedFilterCategory, _allEntries[item])
+ .IndexOf(filterText, StringComparison.OrdinalIgnoreCase) >= 0);
+ }
+ return matches.ToList();
+ }
///
/// Find the indices of entries matching the filter text according to the filter type
@@ -278,33 +342,49 @@ public IList Filter(string filterText, string filterCategory)
// We have to deal with the UnmodifiedTargetText separately from the adduct because the
// adduct has special sorting which is different than the way adduct.ToString() would sort.
- // Find the indices of entries that have a field that could match the search term if something was appended to it
- var filteredIndices = PrefixSearchByProperty(filterText);
-
- // Special filtering for numeric properties
- if (double.TryParse(filterText, NumberStyles.Any, CultureInfo.CurrentCulture, out var result) && _continuousFields.Contains(_selectedFilterCategory))
+ List filteredIndices;
+ var isWildcard = filterText.StartsWith(@"*");
+ if (isWildcard)
{
- // Add entries that are close to the filter text numerically
- // Create a list of object references sorted by their absolute difference from target
- var sortedByDifference = _allEntries.OrderBy(entry => Math.Abs(double.Parse(GetStringValue(_selectedFilterCategory, entry), NumberStyles.Any, CultureInfo.CurrentCulture) - result));
-
- // Then return everything before the first entry with a difference exceeding our match tolerance
- var results = sortedByDifference.TakeWhile(entry => !(Math.Abs(
- double.Parse(GetStringValue(_selectedFilterCategory, entry), NumberStyles.Any, CultureInfo.CurrentCulture) - result) > FILTER_TOLERANCE)).Select(IndexOf).ToList();
- filteredIndices = filteredIndices.Union(results).ToList();
+ filterText = filterText.Substring(1);
}
-
- // If we have not found any matches yet and it is a peptide list look at all the entries which could match
- // the target text, if they had something appended to them.
- if (!filteredIndices.Any())
+ if (isWildcard && filterText.Length>0)
{
- var range = CollectionUtil.BinarySearch(_allEntries,
- info => string.Compare(info.UnmodifiedTargetText, 0, filterText, 0,
- info.UnmodifiedTargetText.Length,
- StringComparison.OrdinalIgnoreCase));
- // Return the elements from the range whose DisplayText actually matches the filter text.
- return ImmutableList.ValueOf(new RangeList(range).Where(i => _allEntries[i].DisplayText
- .StartsWith(filterText, StringComparison.OrdinalIgnoreCase)));
+ // For strings starting in "*", find the indices of entries that have a field
+ // that contain the search term
+ filteredIndices = ContainsSearchByProperty(filterText);
+ }
+ else
+ {
+ // Otherwise, find the indices of entries that have a field that could match the search term
+ // if something was appended to it
+ filteredIndices = PrefixSearchByProperty(filterText);
+
+ // Special filtering for numeric properties
+ if (double.TryParse(filterText, NumberStyles.Any, CultureInfo.CurrentCulture, out var result) && _continuousFields.Contains(_selectedFilterCategory))
+ {
+ // Add entries that are close to the filter text numerically
+ // Create a list of object references sorted by their absolute difference from target
+ var sortedByDifference = _allEntries.OrderBy(entry => Math.Abs(double.Parse(GetStringValue(_selectedFilterCategory, entry), NumberStyles.Any, CultureInfo.CurrentCulture) - result));
+
+ // Then return everything before the first entry with a difference exceeding our match tolerance
+ var results = sortedByDifference.TakeWhile(entry => !(Math.Abs(
+ double.Parse(GetStringValue(_selectedFilterCategory, entry), NumberStyles.Any, CultureInfo.CurrentCulture) - result) > FILTER_TOLERANCE)).Select(IndexOf).ToList();
+ filteredIndices = filteredIndices.Union(results).ToList();
+ }
+
+ // If we have not found any matches yet and it is a peptide list look at all the entries which could match
+ // the target text, if they had something appended to them.
+ if (!filteredIndices.Any())
+ {
+ var range = CollectionUtil.BinarySearch(_allEntries,
+ info => string.Compare(info.UnmodifiedTargetText, 0, filterText, 0,
+ info.UnmodifiedTargetText.Length,
+ StringComparison.OrdinalIgnoreCase));
+ // Return the elements from the range whose DisplayText actually matches the filter text.
+ return ImmutableList.ValueOf(new RangeList(range).Where(i => _allEntries[i].DisplayText
+ .StartsWith(filterText, StringComparison.OrdinalIgnoreCase)));
+ }
}
// Return the indices of the matches sorted alphabetically by display text
return ImmutableList.ValueOf(filteredIndices.OrderBy(info => info));
diff --git a/pwiz_tools/Skyline/TestFunctional/LibraryExplorerTest.cs b/pwiz_tools/Skyline/TestFunctional/LibraryExplorerTest.cs
index b4b4edc4a8..3bced23494 100644
--- a/pwiz_tools/Skyline/TestFunctional/LibraryExplorerTest.cs
+++ b/pwiz_tools/Skyline/TestFunctional/LibraryExplorerTest.cs
@@ -24,6 +24,7 @@
using System.Linq;
using System.Windows.Forms;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using pwiz.Common.Chemistry;
using pwiz.MSGraph;
using pwiz.Skyline.Alerts;
using pwiz.Skyline.Controls.SeqNode;
@@ -225,18 +226,25 @@ private void TestFilterFunctionality()
Assert.IsTrue(StringComparer.OrdinalIgnoreCase.Compare(x.DisplayText, y.DisplayText) <= 0);
}
+ // Find all that contain chlorine
+ FilterListAndVerifyCount(filterTextBox, pepList, "*Cl", 3);
+
// Entering the formula for Midazolam should filter out all other spectra
var midazolamFormula = "C18H13ClFN3";
FilterListAndVerifyCount(filterTextBox, pepList, midazolamFormula, 1);
+ FilterListAndVerifyCount(filterTextBox, pepList, midazolamFormula.Replace("C18", "*"), 1); // Wildcard
// Check case insensitivity
FilterListAndVerifyCount(filterTextBox, pepList, midazolamFormula.ToLowerInvariant(), 1);
+ FilterListAndVerifyCount(filterTextBox, pepList, midazolamFormula.ToLowerInvariant().Replace("c18", "*"), 1);
// Clearing search box should bring up every entry
FilterListAndVerifyCount(filterTextBox, pepList, "", 6);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*", 6); // Should have same effect as no filter
// Entering 'SD' should filter out all entries as nothing starts with SD
FilterListAndVerifyCount(filterTextBox, pepList, "SD", 0);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*SD", 0); // Nor does anything contain SD
// Clearing the filter text box should bring up every entry
FilterListAndVerifyCount(filterTextBox, pepList, "", 6);
@@ -253,8 +261,10 @@ private void TestFilterFunctionality()
var midazolamMzStr = midazolamMz.ToString("G", CultureInfo.CurrentCulture);
var inexactMidazolamMzStr = (midazolamMz + 0.05).ToString("G", CultureInfo.CurrentCulture);
- // Entering '32' should filter the list down to three entries
+ // Entering '32' should filter the list down to two entries
FilterListAndVerifyCount(filterTextBox, pepList, midazolamMzStr.Substring(0, 2), 2);
+ // Entering '*26.0' should yield just one entry
+ FilterListAndVerifyCount(filterTextBox, pepList, midazolamMzStr.Replace("326", "*26").Substring(0, 4), 1);
// Entering the exact precursor m/z of Midazolam should narrow the list down to only Midazolam
FilterListAndVerifyCount(filterTextBox, pepList, midazolamMzStr, 1);
@@ -270,6 +280,7 @@ private void TestFilterFunctionality()
filterCategoryComboBox.FindStringExact("cas");
});
FilterListAndVerifyCount(filterTextBox, pepList, "4928", 1);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*928", 1); // Wildcard
// Now switch to a list with multiple molecular IDs
RunUI(() => { libComboBox.SelectedIndex = libComboBox.FindStringExact(MULTIPLE_MOL_IDS); });
@@ -295,6 +306,7 @@ private void TestFilterFunctionality()
});
FilterListAndVerifyCount(filterTextBox, pepList, "123", 1);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*23", 1); // Wildcard
// Now test search behavior on a peptide list
@@ -305,6 +317,8 @@ private void TestFilterFunctionality()
// Searching for a peptide sequence should work as well
FilterListAndVerifyCount(filterTextBox, pepList, "CY", 2);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*CY", 31); // Wildcard
+ FilterListAndVerifyCount(filterTextBox, pepList, "*Y", 80); // Wildcard
// Now test filtering by precursor m/z
RunUI(() =>
@@ -315,6 +329,7 @@ private void TestFilterFunctionality()
// Precursor searching should work here as well
FilterListAndVerifyCount(filterTextBox, pepList, "6", 13);
+ FilterListAndVerifyCount(filterTextBox, pepList, "*" + (6.4).ToString("G", CultureInfo.CurrentCulture), 3);
// Switch to library with both molecules and peptides
ShowDialog(
@@ -323,9 +338,34 @@ private void TestFilterFunctionality()
// Verify that we found all of the filter categories
expectedCategories = new List(categories);
- expectedCategories.AddRange(new List{ Resources.PeptideTipProvider_RenderTip_Ion_Mobility , Resources.PeptideTipProvider_RenderTip_CCS , "InChI", smiles});
+ var inchi = "InChI";
+ expectedCategories.AddRange(new List { Resources.PeptideTipProvider_RenderTip_Ion_Mobility, Resources.PeptideTipProvider_RenderTip_CCS, inchi, smiles });
VerifyFilterCategories(filterCategoryComboBox, expectedCategories);
+ // * Match IM units
+ RunUI(() =>
+ {
+ filterCategoryComboBox.SelectedIndex =
+ filterCategoryComboBox.FindStringExact(Resources.PeptideTipProvider_RenderTip_Ion_Mobility);
+ });
+ FilterListAndVerifyCount(filterTextBox, pepList,
+ "*" + IonMobilityValue.GetUnitsString(eIonMobilityUnits.drift_time_msec), 2);
+
+ // Match SMILES
+ RunUI(() =>
+ {
+ filterCategoryComboBox.SelectedIndex = filterCategoryComboBox.FindStringExact(smiles);
+ });
+ FilterListAndVerifyCount(filterTextBox, pepList, "*=", 1);
+
+ // Match InChI
+ RunUI(() =>
+ {
+ filterCategoryComboBox.SelectedIndex = filterCategoryComboBox.FindStringExact(inchi);
+ });
+ FilterListAndVerifyCount(filterTextBox, pepList, "*C32", 1);
+
+
// Close the spectral library explorer
OkDialog(_viewLibUI , _viewLibUI.CancelDialog);
}