private ProteinMatchSettings CreateProteinMatchSettings(SrmDocument srmDocument, ProteinMatchTypes matchTypes, String searchText)
        {
            var peptideSettings    = srmDocument.Settings.PeptideSettings;
            var backgroundProteome = peptideSettings.BackgroundProteome;

            if (backgroundProteome.IsNone)
            {
                return(null);
            }
            try
            {
                if (_proteomeDb != null && _proteomeDb.Path != backgroundProteome.DatabasePath)
                {
                    _proteomeDb.Dispose();
                    _proteomeDb = null;
                }
                if (_proteomeDb == null)
                {
                    _proteomeDb = backgroundProteome.OpenProteomeDb(_cancellationTokenSource.Token);
                }
                return(new ProteinMatchSettings(_proteomeDb.ProteomeDbPath,
                                                matchTypes,
                                                searchText));
            }
            catch (SQLiteException)
            {
                // CONSIDER: Silent failure could be confusing.  Show a message box
                //           about failing to open the database.
                return(null);
            }
        }
        [MethodImpl(MethodImplOptions.NoOptimization)] // TODO(nicksh): reenable optimizations after we track down a NullReferenceException
        public static IList <ListViewItem> CreateListViewItems(IList <ProteinMatch> matches, String searchText, ProteinMatchTypes matchTypes, PeptideSettings peptideSettings, int maxCount)
        {
            var listItems      = new SortedList <string, ListViewItem>();
            var setUsedMatches = new HashSet <string>();

            // First check for matching by sequence
            foreach (var match in matches)
            {
                if (matchTypes.Contains(ProteinMatchType.sequence))
                {
                    HashSet <String> addedPeptideSequences = new HashSet <string>();
                    FastaSequence    fastaSequence;
                    try
                    {
                        fastaSequence = new FastaSequence("name", "description", new ProteinMetadata[0], match.Protein.Sequence); // Not L10N
                    }
                    catch (InvalidDataException)
                    {
                        // It's possible that the peptide sequence in the fasta file was bogus, in which case we just don't digest it.
                        continue;
                    }
                    foreach (Peptide peptide in peptideSettings.Enzyme.Digest(fastaSequence, peptideSettings.DigestSettings))
                    {
                        if (!peptide.Sequence.StartsWith(searchText))
                        {
                            continue;
                        }
                        if (!addedPeptideSequences.Add(peptide.Sequence))
                        {
                            continue;
                        }
                        var listItem = new ListViewItem
                        {
                            Text = peptide.Sequence,
                            Tag  = new StatementCompletionItem
                            {
                                Peptide     = peptide.Sequence,
                                ProteinInfo = match.Protein.ProteinMetadata,
                                SearchText  = searchText
                            },
                        };
                        StatementCompletionForm.AddDescription(listItem,
                                                               match.Protein.ProteinMetadata.TextForMatchTypes(matchTypes),
                                                               null);
                        setUsedMatches.Add(match.Protein.Name);
                        listItem.ImageIndex = (int)ImageId.peptide;
                        var tooltip = new StringBuilder();
                        tooltip.AppendLine(Resources.StatementCompletionTextBox_CreateListViewItems_Descriptions)
                        .Append(match.Protein.ProteinMetadata.TextForMatchTypes(matchTypes));
                        foreach (var name in match.Protein.AlternativeNames)
                        {
                            tooltip.AppendLine().Append(name.TextForMatchTypes(matchTypes));
                        }
                        listItem.ToolTipText = StripTabs(tooltip.ToString());
                        // Note the leading space in this sort key - we'd like to list sequence matches first
                        var key = TextUtil.SpaceSeparate(" ", listItem.Text, listItem.ToolTipText); // Not L10N
                        if (!listItems.ContainsKey(key))
                        {
                            listItems.Add(key, listItem);
                        }
                    }
                }
            }
            if (listItems.Count >= maxCount)
            {
                return(new List <ListViewItem>(listItems.Values));  // We used to exit here if we had any matches - but that's frustrating when you're not actually trying to match by sequence
            }

            // Decide which field not to display on righthand side, based on what's already showing on the left due to View|Targets|By* menu
            ProteinMatchTypes displayMatchTypes = ProteinMatchTypes.ALL;

            switch (SequenceTree.ProteinsDisplayMode)
            {
            case ProteinMetadataManager.ProteinDisplayMode.ByName:
                displayMatchTypes = displayMatchTypes.Except(ProteinMatchType.name);
                break;

            case ProteinMetadataManager.ProteinDisplayMode.ByAccession:
                displayMatchTypes = displayMatchTypes.Except(ProteinMatchType.accession);
                break;

            case ProteinMetadataManager.ProteinDisplayMode.ByGene:
                displayMatchTypes = displayMatchTypes.Except(ProteinMatchType.gene);
                break;

            case ProteinMetadataManager.ProteinDisplayMode.ByPreferredName:
                displayMatchTypes = displayMatchTypes.Except(ProteinMatchType.preferredName);
                break;
            }

            ProteinMatchTypes secondPassMatchTypes = matchTypes.Except(
                ProteinMatchType.sequence,     // We already did sequence
                ProteinMatchType.description); // And aren't ready for description

            foreach (var match in matches)
            {
                if (setUsedMatches.Contains(match.Protein.Name))
                {
                    continue;
                }
                // Try matching on name, accession etc - cycle through name, accession, preferredName, gene
                foreach (ProteinMatchType tryType in secondPassMatchTypes)
                {
                    if (match.MatchTypes.Contains(tryType))
                    {
                        var listItem = new ListViewItem();
                        // Show description, and any other fields we were searching on
                        if (match.AlternativeName != null)
                        {
                            listItem.Text = ProteinMetadataManager.ProteinModalDisplayText(match.AlternativeName, Settings.Default.ShowPeptidesDisplayMode);
                            listItem.Tag  = new StatementCompletionItem {
                                ProteinInfo = match.AlternativeName, SearchText = searchText
                            };
                            StatementCompletionForm.AddDescription(listItem,
                                                                   match.AlternativeName.TextForMatchTypes(displayMatchTypes.Except(ProteinMatchType.name)), searchText);
                        }
                        else
                        {
                            listItem.Text = ProteinMetadataManager.ProteinModalDisplayText(match.Protein.ProteinMetadata, Settings.Default.ShowPeptidesDisplayMode);
                            listItem.Tag  = new StatementCompletionItem {
                                ProteinInfo = match.Protein.ProteinMetadata, SearchText = searchText
                            };
                            StatementCompletionForm.AddDescription(listItem,
                                                                   match.Protein.ProteinMetadata.TextForMatchTypes(displayMatchTypes), searchText);
                        }
                        setUsedMatches.Add(match.Protein.Name);
                        listItem.ImageIndex = (int)ImageId.protein;
                        var tooltip = new StringBuilder();
                        tooltip.AppendLine(Resources.StatementCompletionTextBox_CreateListViewItems_Descriptions)
                        .Append(match.Protein.ProteinMetadata.TextForMatchTypes(displayMatchTypes));
                        foreach (var altName in match.Protein.AlternativeNames)
                        {
                            tooltip.AppendLine().Append(altName.TextForMatchTypes(displayMatchTypes));
                        }
                        listItem.ToolTipText = StripTabs(tooltip.ToString());
                        // We want the sort to be on the particular bit of metadata that we matched
                        var key = TextUtil.SpaceSeparate(match.Protein.ProteinMetadata.TextForMatchTypes(ProteinMatchTypes.Singleton(tryType)),
                                                         listItem.Text, listItem.ToolTipText);
                        if (!listItems.ContainsKey(key))
                        {
                            listItems.Add(key, listItem);
                        }
                        break;
                    }
                }
            }
            if (listItems.Count >= maxCount)
            {
                return(new List <ListViewItem>(listItems.Values));  // We used to exit here if we had any matches - but that's frustrating when you're not actually trying to match by sequence
            }

            // Any matches by description?
            foreach (var match in matches)
            {
                if (setUsedMatches.Contains(match.Protein.Name))
                {
                    continue;
                }
                if (match.MatchTypes.Contains(ProteinMatchType.description))
                {
                    ProteinMetadata mainName    = match.AlternativeDescription;
                    string          matchName   = match.Protein.Name;
                    var             proteinInfo = match.Protein.ProteinMetadata;
                    if (matchName != null && matchName.Length > MAX_NAME_LENGTH)
                    {
                        proteinInfo = proteinInfo.ChangeName(matchName.Substring(0, MAX_NAME_LENGTH) + "..."); // Not L10N
                    }
                    var alternativeNames = new List <ProteinMetadata>();
                    if (mainName == null)
                    {
                        mainName = proteinInfo;
                    }
                    else
                    {
                        alternativeNames.Add(proteinInfo);
                    }
                    var listItem = new ListViewItem
                    {
                        Text       = ProteinMetadataManager.ProteinModalDisplayText(mainName, Settings.Default.ShowPeptidesDisplayMode),
                        ImageIndex = (int)ImageId.protein,
                        Tag        = new StatementCompletionItem {
                            ProteinInfo = proteinInfo, SearchText = searchText
                        }
                    };

                    StatementCompletionForm.AddDescription(listItem, mainName.TextForMatchTypes(displayMatchTypes), searchText);
                    if (match.Protein.AlternativeNames.Count > 0)
                    {
                        alternativeNames.AddRange(match.Protein.AlternativeNames);
                        StringBuilder tooltip = new StringBuilder(Resources.StatementCompletionTextBox_CreateListViewItems_Alternative_Names);
                        foreach (var altName in alternativeNames)
                        {
                            if (altName.Name == mainName.Name)
                            {
                                continue;
                            }

                            tooltip.AppendLine().Append(altName.TextForMatchTypes(displayMatchTypes.Union(ProteinMatchType.name)));
                        }
                        listItem.ToolTipText = StripTabs(tooltip.ToString());
                    }
                    // We want the sort to be on what we matched in the description, and what follows.
                    var remains = match.Protein.ProteinMetadata.Description ?? string.Empty;
                    int pos     = remains.ToLower().IndexOf(searchText.ToLower(), StringComparison.Ordinal);
                    if (pos > 0)
                    {
                        remains = remains.Substring(pos);
                    }
                    var key = TextUtil.SpaceSeparate(remains, listItem.Text, listItem.ToolTipText);
                    if (!listItems.ContainsKey(key))
                    {
                        listItems.Add(key, listItem);
                    }
                }
            }
            return(new List <ListViewItem>(listItems.Values));
        }