// Find occurences of the search string in a block of text, such as a program,
        // log file or note
        private List <SearchMatch> matchTextContent(
            string text,
            string searchString,
            string filename,
            string processFlowName,
            string type,
            string label
            )
        {
            List <SearchMatch> matches = new List <SearchMatch>();
            Regex ex = new Regex(searchString.Trim(), RegexOptions.IgnoreCase | RegexOptions.Singleline);

            string[] lines      = Regex.Split(text, "\r\n");
            int      lineNumber = 0;

            foreach (string line in lines)
            {
                lineNumber++;
                if (ex.Match(line).Captures.Count > 0)
                {
                    SearchMatch match = new SearchMatch()
                    {
                        ProjectFile = filename,
                        ItemLabel   = label,
                        ProcessFlow = processFlowName,
                        Location    = string.Format("Line {0}", lineNumber),
                        ItemType    = type
                    };
                    match.MatchedLine = line;
                    matches.Add(match);
                }
            }

            return(matches);
        }
        // Save the results to a text file
        private void SaveResults(string outFile)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("Searched {0} for text matching \"{1}\"\r\n", txtFilespec.Text, txtSearchFor.Text);
            sb.AppendFormat("Total matches found: {0}\r\n", lvMatches.Items.Count);
            sb.AppendLine("-------------------------------------");
            foreach (ListViewItem item in lvMatches.Items)
            {
                SearchMatch m = item.Tag as SearchMatch;
                if (m != null)
                {
                    sb.AppendFormat("{0} (process flow: {1}): {2} ({3}) - {4} \"{5}\" \r\n",
                                    m.ProjectFile, m.ProcessFlow, m.ItemLabel, m.ItemType, m.Location, m.MatchedLine);
                }
            }

            try
            {
                System.IO.File.WriteAllText(outFile, sb.ToString());
                MessageBox.Show(string.Format("Results saved to {0}", outFile));
            }
            catch
            {
                MessageBox.Show(string.Format("Unable to save results to {0}", outFile));
            }
        }
        /// <summary>
        /// Create a list view item from one of our "match" objects
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="m"></param>
        /// <returns></returns>
        private static ListViewItem ListItemFromMatch(string filename, SearchMatch m)
        {
            string       file   = Path.GetFileName(filename);
            string       folder = Path.GetDirectoryName(filename);
            ListViewItem item   = new ListViewItem
                                      (new string[] { m.ItemLabel, m.ItemType, m.ProcessFlow, m.Location, file, folder, m.MatchedLine });

            item.Tag = m;
            return(item);
        }
        // make the context menu "dynamic" with the project file name
        private void onContextMenuOpened(object sender, EventArgs e)
        {
            ListViewItem item = null;

            if (lvMatches.SelectedItems.Count > 0)
            {
                item = lvMatches.SelectedItems[0];
            }

            if (item != null && item.Tag != null)
            {
                openProjectFile.Enabled = true;
                SearchMatch m = item.Tag as SearchMatch;

                openProjectFile.Text = string.Format("Open \"{0}\" in SAS Enterprise Guide", m.ProjectFile);
                openProjectFile.Tag  = m.ProjectFile;
            }
            else
            {
                openProjectFile.Enabled = false;
                openProjectFile.Tag     = null;
            }
        }
        /// <summary>
        /// Search a project file for occurences of the specified string
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="searchString"></param>
        /// <returns></returns>
        private List <SearchMatch> SearchProject(string filename, string searchString)
        {
            List <SearchMatch> matches = new List <SearchMatch>();

            try
            {
                SAS.EG.Automation.EGAutomation.StartEG();
                SAS.EG.Automation.EGAutomation.OpenProject(filename, "");
                foreach (SAS.EG.Scripting.Container c in SAS.EG.Automation.EGAutomation.EGProject.ContainerCollection)
                {
                    if (c.ContainerType == 0)
                    {
                        foreach (SAS.EG.Scripting.ISASEGItem item in c.Items)
                        {
                            // grab just the last part of the type
                            string   type   = item.GetType().ToString();
                            string[] tokens = type.Split(new char[] { '.' });
                            if (tokens.Length > 0)
                            {
                                type = tokens[tokens.Length - 1];
                            }

                            if (matchName(item.Name, searchString))
                            {
                                SearchMatch match = new SearchMatch()
                                {
                                    ProjectFile = filename,
                                    ItemLabel   = item.Name,
                                    ProcessFlow = c.Name,
                                    Location    = "Label",
                                    ItemType    = type
                                };
                                match.MatchedLine = item.Name;

                                matches.Add(match);
                            }

                            matches.AddRange(matchContent(item, c, filename, searchString));
                        }

                        // for handling Note objects, which come in a different collection
                        foreach (SAS.EG.Scripting.ISASEGNote item in c.Notes)
                        {
                            if (matchName(item.Name, searchString))
                            {
                                SearchMatch match = new SearchMatch()
                                {
                                    ProjectFile = filename,
                                    ItemLabel   = item.Name,
                                    ProcessFlow = c.Name,
                                    Location    = "Label",
                                    ItemType    = "Note"
                                };
                                match.MatchedLine = item.Name;

                                matches.Add(match);
                            }

                            matches.AddRange(matchContent(item, c, filename, searchString));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // capture just the first line of a long message
                string[] parts   = Regex.Split(ex.Message, "\r\n");
                string   message = parts[0];
                txtMessages.AppendText(string.Format("Error searching {0}: {1}\r\n", filename, message));
            }
            finally
            {
                SAS.EG.Automation.EGAutomation.EndEG();
            }

            return(matches);
        }