Click here to Skip to main content
15,880,796 members
Articles / Programming Languages / C#

The Grid Processor: Word Processing Abilities for the .NET DataGridView Component

Rate me:
Please Sign up or sign in to vote.
4.87/5 (15 votes)
7 May 200711 min read 65.6K   1.6K   51  
A plugin which offers search and replace, casing and other capabilities for the Microsoft .NET DataGridView component
#region GNU notice
// GridProcessor - Search and replace abilities for the .NET DataGridView
// Copyright (C) 2007, by Evan Stein
// http://www.sourceforge.net/projects/grid-processor
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
#endregion GNU notice
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace GridProcessor
{
    public partial class FormGridProcessor : Form
    {
        // Reference to grid we're acting on
        private DataGridView mGrid;
        // Our copy of grid's metadata
        private Dictionary<string, DataGridViewColumn> mGridColumns = new Dictionary<string, DataGridViewColumn>();
        // Module-wide structures for operations
        private Replacement mSearchReplacement = new Replacement();
        private Replacement mCaseReplacement = new Replacement();
        private Replacement mFind = new Replacement();
        private Macro mMacro = new Macro();
        private Split mSplit = new Split();
        private FormReplaceConfirm mFormReplaceConfirm = null;
        private Settings mSettings = null;
        private int mColumnsSelected = 0;

        #region Constructor, Initialization and UI

        /// <summary>
        /// The main "grid processor" Windows form
        /// </summary>
        /// <param name="callingGrid">The grid on which this component operates</param>
        public FormGridProcessor(DataGridView callingGrid)
        {
            InitializeComponent();
            // Set the internal grid
            mSettings = new Settings();
            mGrid = callingGrid;
            Initialize();
        }

        /// <summary>
        /// The main "grid processor" Windows form
        /// </summary>
        /// <param name="callingGrid">The grid on which this component operates</param>
        /// <param name="settings">User-crafted database settings</param>
        public FormGridProcessor(DataGridView callingGrid, Settings settings)
        {
            InitializeComponent();
            // Set the internal grid
            mSettings = settings;
            mGrid = callingGrid;
            Initialize();
        }

        /// <summary>
        /// Startup routines common after different constructors
        /// </summary>
        private void Initialize()
        {
            // Figure out the grid structure
            GetGridColumns();

            // Retrieve the list of macros that will be visible
            // on Macros tab

            // Overwrite the hard-coded connection string
            oleDbConnection1.ConnectionString = mSettings.ConnectionString;
            // Retrieve available replacement scripts

            try
            {
                oleDbDataAdapter_macro.Fill(dataSet_macro1, "Macro");
            }
            catch (Exception ex)
            {
                throw new Exception("Data error loading macros. " + ex.Message);
            }
        }

        /// <summary>
        /// Figures out the grid structure, and chooses the columns
        /// we want to work on
        /// </summary>
        private void GetGridColumns()
        {
            DataGridViewColumn col;
            DataTable dtSplitResult;
            DataRow newRow;
            List<Type> AllowedColumnTypes = new List<Type>();
            List<Type> AllowedValueTypes = new List<Type>();

            dtSplitResult = dataSet_splitResult.Tables["SplitResult"];

            // Exclude picture and other unreadable column types
            AllowedColumnTypes.Add(typeof(DataGridViewComboBoxCell));
            AllowedColumnTypes.Add(typeof(DataGridViewTextBoxCell));

            // Allow only data types we can deal with
            AllowedValueTypes.Add(typeof(string));
            // Possible addition - work with numbers and dates

            // Get rid of any "example" metadata in our own
            // screen layout
            this.checkedListBox_columns.Items.Clear();
            for (int i = 0; i < mGrid.Columns.Count; i++)
            {
                col = mGrid.Columns[i];
                if (!col.Visible)
                    continue;
                if (!AllowedColumnTypes.Contains(col.CellType))
                    continue;
                if (!AllowedValueTypes.Contains(col.ValueType))
                    continue;
                mGridColumns.Add(col.HeaderText, col);
                // Add to listbox, so user can choose columns
                checkedListBox_columns.Items.Add(col.HeaderText, false);
                newRow = dtSplitResult.NewRow();
                newRow["ColumnName"] = col.HeaderText;
                dtSplitResult.Rows.Add(newRow);
                comboBox_splitSource.Items.Add(col.HeaderText);
            }
            checkedListBox_columns.Sorted = true; // just in case it isn't
        }

        private void button_help_Click(object sender, EventArgs e)
        {
            string helpFileName;

            helpFileName = Util.GetRootApplicationDirectory();
            if (helpFileName[helpFileName.Length - 1] != '\\')
                helpFileName += "\\";
            helpFileName += "GridProcessor.chm";
            System.Windows.Forms.Help.ShowHelp(this, helpFileName);
        }

        private void button_close_Click(object sender, EventArgs e)
        {
            this.Hide();
        }

        private void FormGridProcessor_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Allow anyone but the user to close this form

            // If the user clicked the "x"
            if (e.CloseReason == CloseReason.UserClosing)
            {
                this.Hide();    // make them think they succeeded!
                e.Cancel = true;
            }
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            SetTabControlsCurrent();
        }

        private void SetTabControlsCurrent()
        {
            string tabText;

            tabText = tabControl1.SelectedTab.Text;
            switch (tabText)
            {
                case "Find": SetTabControlsFind(); break;
                case "Search and Replace": SetTabControlsSearchReplace(); break;
                case "Upper/Lower Case": SetTabControlsCase(); break;
                case "Split": SetTabControlsSplit(); break;
                case "Macros": SetTabControlsMacro(); break;
            }
        }

        #endregion Constructor, Initialization and UI

        #region Find functionality

        private void button_findFirst_Click(object sender, EventArgs e)
        {
            FindResult result;
            //Find();
            result = FindNext(-1, 0, true);
            if (result.Success)
                mGrid.CurrentCell = mGrid[result.ColumnIndex, result.RowIndex];
            else
                MessageBox.Show(this, "Not found", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void button_findNext_Click(object sender, EventArgs e)
        {
            FindResult result;
            result = FindNext(mGrid.CurrentCell.ColumnIndex, mGrid.CurrentCell.RowIndex, true);
            if (result.Success)
                mGrid.CurrentCell = mGrid[result.ColumnIndex, result.RowIndex];
            else
                MessageBox.Show(this, "Not found", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void button_findCount_Click(object sender, EventArgs e)
        {
            FindCount();
        }

        /// <summary>
        /// Gathers the data we need before attempting to find 
        /// something.
        /// </summary>
        /// <returns>Success</returns>
        private bool GetFindInfo()
        {
            mFind.CaseSensitive = checkBox_findCaseSensitive.Checked;
            mFind.SearchExpression = textBox_findFindExpression.Text;
            mFind.UseRegex = checkBox_findRegularExpression.Checked;
            if (mFind.SearchExpression.Length == 0)
            {
                MessageBox.Show(this, "Please supply a search expression", "Find", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            return true;
        }

        /// <summary>
        /// Returns a list of checked column indices, sorted by number
        /// </summary>
        /// <returns></returns>
        private int[] GetFindColumnIndices()
        {
            int columnsChecked;
            int[] columnIndices;
            string gridCaption;

            if ((columnsChecked = checkedListBox_columns.CheckedItems.Count) == 0)
            {
                MessageBox.Show(this, "No columns selected to search.", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
                columnIndices = new int[0];
            }
            else
            {
                columnIndices = new int[columnsChecked];
                for (int i = 0; i < columnsChecked; i++)
                {
                    gridCaption = checkedListBox_columns.CheckedItems[i].ToString();
                    columnIndices[i] = mGridColumns[gridCaption].Index;
                }
                // Do our searching in column, rather than alphabetical order
                Array.Sort(columnIndices);

            }
            return columnIndices;
        }

        /// <summary>
        /// Reports the number of cells that have the search expression 
        /// (which may or may not have multiple instances)
        /// </summary>
        private void FindCount()
        {
            int[] columnIndices;
            int foundCount = 0;
            int saveRow, saveColumn;
            FindResult findResult;

            if (!GetFindInfo())
                return;
            columnIndices = GetFindColumnIndices();
            if (columnIndices.Length == 0)
                return;
            saveRow = mGrid.CurrentCell.RowIndex;
            saveColumn = mGrid.CurrentCell.ColumnIndex;

            for (int i = 0; i < mGrid.RowCount; i++)
            {
                for (int j = 0; j < columnIndices.Length; j++)
                {
                    findResult = FindValue(mFind, i, columnIndices, j);
                    if (!findResult.Success)
                        break;
                    foundCount++;
                }
            }
            MessageBox.Show(this, foundCount.ToString(), "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void SetTabControlsFind()
        {
            if (mColumnsSelected == 0)
            {
                button_findCount.Enabled = false;
                button_findFirst.Enabled = false;
                button_findNext.Enabled = false;
            }
            if (textBox_findFindExpression.Text.Length > 0)
            {
                if (mColumnsSelected > 0)
                {
                    button_findCount.Enabled = true;
                    button_findFirst.Enabled = true;
                    button_findNext.Enabled = true;
                }
                if (tabControl1.SelectedTab.Text == "Find")
                {
                    if (textBox_testString.Text.Length > 0)
                        button_test.Enabled = true;
                    else
                        button_test.Enabled = false;
                }
            }
            else
                button_test.Enabled = false;
        }

        /// <summary>
        /// Finds the first instance of an item in the grid. Highlights
        /// the item if found.
        /// </summary>
        private void Find()
        {
            int searchColOffset, searchRow;
            int[] columnIndices;
            bool found = false;
            FindResult result;

            if (!GetFindInfo())
                return;
            columnIndices = GetFindColumnIndices();
            if (columnIndices.Length == 0)
                return;

            searchRow = 0;
            searchColOffset = 0;

            do
            {
                result = FindValue(mFind, searchRow, columnIndices, searchColOffset);
                if (!result.Success)
                {
                    //    result = DoFind(mGrid.CurrentCell.Value.ToString(), mFind);
                    //                    if (result.Success)
                    //                      break;
                    searchRow++;
                    searchColOffset = 0;
                }
                else
                {
                    mGrid.CurrentCell = mGrid[result.ColumnIndex, result.RowIndex];
                    found = true;
                    break;
                }
            } while (searchRow < mGrid.RowCount);

            if (!found)
            {
                MessageBox.Show(this, "Not found", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// Finds the next occurrence of the search item in the grid,
        /// ignoring the current cell. Does not wrap to the beginning
        /// if at the end.
        /// </summary>
        /// <param name="startColumn">Starting grid column (-1 to start from beginning)</param>
        /// <param name="startRow">Starting grid row</param>
        /// <param name="interactive">Give UI cues?</param>
        /// <returns>Success</returns>
        private FindResult FindNext(int startColumn, int startRow, bool interactive)
        {
            if (!GetFindInfo())
                return new FindResult();    // initialized to false

            return FindNext(mFind, startColumn, startRow, interactive);
        }

        /// <summary>
        /// Finds the next occurrence of the search item in the grid,
        /// ignoring the current cell. Does not wrap to the beginning
        /// if at the end.
        /// </summary>
        /// <param name="findInfo">A replacement object that tells us what to look for</param>
        /// <param name="startColumn">Starting grid column (-1 to start from beginning)</param>
        /// <param name="startRow">Starting grid row</param>
        /// <param name="interactive">Give UI cues?</param>
        /// <returns>Success</returns>
        private FindResult FindNext(Replacement findInfo, int startColumn, int startRow, bool interactive)
        {
            int searchColOffset, searchRow, currentColumn;
            int[] columnIndices;
            FindResult findResult, notFound;
            DialogResult dialogResult;

            // Success initialized to false
            notFound = new FindResult();

            columnIndices = GetFindColumnIndices();
            if (columnIndices.Length == 0)
                return notFound;

            // Figure out where we are in the grid, get the
            // next place to start looking
            searchRow = startRow;
            currentColumn = startColumn;
            searchColOffset = -1;
            for (int i = 0; i < columnIndices.Length; i++)
            {
                if (columnIndices[i] > currentColumn)
                {
                    searchColOffset = i;
                    break;
                }
            }
            if (searchColOffset == -1)
            {
                searchColOffset = 0;
                searchRow++;
                // Wraparound
            }
            while (true)
            {
                // If we got duff data, we increased the number ourselves
                if (searchRow >= mGrid.RowCount)
                {
                    // If the user has called this from the UI we
                    // can do something cute
                    if (interactive)
                    {
                        dialogResult = MessageBox.Show(this, "The search reached the bottom. Wrap to the top?", "Find", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                        if (dialogResult == DialogResult.Yes)
                            searchRow = 0;
                        else
                            return notFound;
                    }
                    else
                        // If called from a routine, we've reached the end
                        return notFound;
                }

                findResult = FindValue(findInfo, searchRow, columnIndices, searchColOffset);
                // If we got the same cell back
                if (!findResult.Success)
                {
                    searchRow++;
                    searchColOffset = 0;
                }
                else
                    break;
            }
            return findResult;
        }

        /// <summary>
        /// Finds a value at row level
        /// </summary>
        /// <param name="findInfo">Replacement object with find information</param>
        /// <param name="rowNum">Row to search</param>
        /// <param name="columnIndices">An array of column indices to search</param>
        /// <param name="startOffset">First index to use</param>
        /// <returns></returns>
        private FindResult FindValue(Replacement findInfo, int rowNum, int[] columnIndices, int startOffset)
        {
            object cellValue;
            string stringValue;
            int searchColOffset;
            FindResult result;

            // Default value - not found
            result = new FindResult();

            searchColOffset = startOffset;
            if (!findInfo.CaseSensitive)
            {
                if (!findInfo.UseRegex)
                    findInfo.SearchExpression = findInfo.SearchExpression.ToUpper();
            }
            for (int i = startOffset; i < columnIndices.Length; i++)
            {
                cellValue = mGrid[columnIndices[i], rowNum].Value;
                if (cellValue == null)
                    continue;
                stringValue = cellValue.ToString();
                result = DoFind(stringValue, findInfo);
                if (result.Success)
                {
                    result.RowIndex = rowNum;
                    result.ColumnIndex = columnIndices[i];
                    break;
                }
            }
            return result;
        }

        private FindResult DoFind(string stringToSearch, Replacement findInfo)
        {
            Match match;
            RegexOptions options;
            FindResult result;
            int pos;

            result = new FindResult();

            if (findInfo.UseRegex)
            {
                if (!findInfo.CaseSensitive)
                    options = RegexOptions.IgnoreCase;
                else
                    options = RegexOptions.None;

                match = Regex.Match(stringToSearch, findInfo.SearchExpression, options);
                if (match.Success)
                {
                    result.Success = true;
                    result.Index = match.Index;
                    result.Length = match.Length;
                }
            }
            else
            {
                if (!findInfo.CaseSensitive)
                    pos = stringToSearch.ToUpper().IndexOf(findInfo.SearchExpression);
                else
                    pos = stringToSearch.IndexOf(findInfo.SearchExpression);

                if (pos >= 0)
                {
                    result.Success = true;
                    result.Index = pos;
                    result.Length = findInfo.SearchExpression.Length;
                }
            }
            return result;
        }

        private void TestFind()
        {
            FindResult findResult;

            if (!GetFindInfo())
                return;
            textBox_resultString.Clear();
            if (!mFind.CaseSensitive && !mFind.UseRegex)
                mFind.SearchExpression = mFind.SearchExpression.ToUpper();
            findResult = DoFind(textBox_testString.Text, mFind);
            if (findResult.Success)
            {
                textBox_testString.Focus();
                //MessageBox.Show(this, "Success", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
                textBox_testString.Select(findResult.Index, findResult.Length);
            }
            else
                MessageBox.Show(this, "Not found", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        private void textBox_findFindExpression_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsFind();

        }

        #endregion Find functionality

        #region Search and Replace functionality

        private void SearchAndReplace(Replacement replacement, bool interactive)
        {
            int columnsChecked;
            int[] columnIndices;
            string gridCaption, originalValue, replacementValue;
            int startCol, startRow;
            FindResult findResult;
            bool replaceInteractively;
            int replacementCount;

            if ((columnsChecked = checkedListBox_columns.CheckedItems.Count) == 0)
            {
                MessageBox.Show(this, "No columns selected to work on.", "Search and Replace", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            replaceInteractively = interactive;
            replacementCount = 0;

            columnIndices = new int[columnsChecked];
            for (int i = 0; i < columnsChecked; i++)
            {
                gridCaption = checkedListBox_columns.CheckedItems[i].ToString();
                columnIndices[i] = mGridColumns[gridCaption].Index;
            }
            if (radioButton_all.Checked)
            {
                startCol = -1;
                startRow = 0;
                while (true)
                {
                    findResult = FindNext(replacement, startCol, startRow, replaceInteractively);
                    if (!findResult.Success)
                        break;
                    originalValue = mGrid[findResult.ColumnIndex, findResult.RowIndex].Value.ToString();
                    if (replaceInteractively)
                    {
                        //Information needed for the confirm screen
                        findResult.Replacement = replacement;
                        if (mFormReplaceConfirm == null)
                            mFormReplaceConfirm = new FormReplaceConfirm(mGrid, findResult);
                        else
                            mFormReplaceConfirm.FindResult = findResult;
                        mFormReplaceConfirm.ShowDialog(this);
                        if (mFormReplaceConfirm.Result == ReplaceConfirmResult.Yes)
                        {
                            replacementValue = Util.GetReplacementValue(originalValue, replacement);
                            mGrid[findResult.ColumnIndex, findResult.RowIndex].Value = replacementValue;
                            startCol = findResult.ColumnIndex;
                            startRow = findResult.RowIndex;
                            replacementCount++;
                        }
                        if (mFormReplaceConfirm.Result == ReplaceConfirmResult.Skip)
                        {
                            startCol = findResult.ColumnIndex;
                            startRow = findResult.RowIndex;
                            continue;
                        }
                        if (mFormReplaceConfirm.Result == ReplaceConfirmResult.Cancel)
                        {
                            return;
                        }
                        if (mFormReplaceConfirm.Result == ReplaceConfirmResult.YesAll)
                        {
                            replaceInteractively = false;
                        }
                    }
                    if (!replaceInteractively) // will accommodate change immediately above
                    {
                        replacementValue = Util.GetReplacementValue(originalValue, replacement);
                        mGrid[findResult.ColumnIndex, findResult.RowIndex].Value = replacementValue;
                        startCol = findResult.ColumnIndex;
                        startRow = findResult.RowIndex;
                        replacementCount++;
                    }
                }
                // Using parameter here (on purpose)
                if (interactive)
                {
                    if (replacementCount == 1)
                        MessageBox.Show(this, "1 cell updated", "Search and Replace", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    else
                        MessageBox.Show(this, replacementCount.ToString() + " cells updated", "Search and Replace", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
        }

        /// <summary>
        /// Does a search-and-replace operation on a row in a grid, with
        /// the user-selected columns. Non-interactive.
        /// </summary>
        /// <param name="rowNum">Number of the row</param>
        /// <param name="columnIndices">Array of column indices for action</param>
        /// <param name="findExpr">Expression to locate item</param>
        /// <param name="replaceExpr">The text or Regex operation for replacement</param>
        /// <param name="useRegex">Are the expressions regular expressions?</param>
        /// <param name="caseSensitive">Is the search case-sensitive?</param>
        private void GridRowSearchAndReplace(int rowNum, int[] columnIndices, string findExpr, string replaceExpr, bool useRegex, bool caseSensitive)
        {
            String columnValue, replacedValue;
            object cellValue;

            for (int j = 0; j < columnIndices.Length; j++)
            {
                cellValue = mGrid[columnIndices[j], rowNum].Value;
                if (cellValue == null)
                    return;

                columnValue = cellValue.ToString();
                if (useRegex)
                    replacedValue = Util.ReplaceRegex(columnValue, findExpr, replaceExpr, caseSensitive);
                else
                    replacedValue = Util.ReplaceLiteral(columnValue, findExpr, replaceExpr, caseSensitive);
                if (replacedValue != columnValue)
                    mGrid[columnIndices[j], rowNum].Value = replacedValue;
            }
        }


        /// <summary>
        /// Tests a replacement. Takes the "Test String" item and
        /// fills in the "Result" item with the transformation.
        /// </summary>
        private void TestReplace()
        {
            string resultString;
            bool caseInsensitive, useRegex;

            if (textBox_searchExpr.Text == string.Empty)
            {
                MessageBox.Show("Search expression needs to be filled in.");
                return;
            }
            if (textBox_replaceExpr.Text == string.Empty)
            {
                MessageBox.Show("Replacement expression needs to be filled in.");
                return;
            }
            if (textBox_testString.Text == string.Empty)
            {
                MessageBox.Show("Test string needs to be filled in.");
                return;
            }
            caseInsensitive = checkBox_caseSensitive.Checked;
            useRegex = checkBox_regex.Checked;
            if (useRegex)
                resultString = Util.ReplaceRegex(textBox_testString.Text,
                    textBox_searchExpr.Text, textBox_replaceExpr.Text, caseInsensitive);
            else
                resultString = Util.ReplaceLiteral(textBox_testString.Text,
                    textBox_searchExpr.Text, textBox_replaceExpr.Text, caseInsensitive);

            textBox_resultString.Text = resultString;

        }

        private void button_searchReplaceLoad_Click(object sender, EventArgs e)
        {
            FormLoadReplacementExpression f;
            DialogResult result;

            f = new FormLoadReplacementExpression(mSettings);
            result = f.ShowDialog(this);

            if (result == DialogResult.OK)
                mSearchReplacement = f.Replacement;
            f.Close();

            // Check the database to see whether the item was updated
            mSearchReplacement = Util.ReplacementFactory(mSearchReplacement.ID, mSettings);
            // If missing (deleted), the display will be blanked with empty values
            textBox_setName.Text = mSearchReplacement.Name;
            textBox_searchExpr.Text = mSearchReplacement.SearchExpression;
            textBox_replaceExpr.Text = mSearchReplacement.ReplaceExpression;
            checkBox_regex.Checked = mSearchReplacement.UseRegex;
            checkBox_caseSensitive.Checked = mSearchReplacement.CaseSensitive;

        }

        private void button_searchReplaceSave_Click(object sender, EventArgs e)
        {
            SaveSearchReplaceItem();
        }

        /// <summary>
        /// Saves a Search/Replace operation for later recall
        /// </summary>
        private void SaveSearchReplaceItem() 
        {
            FormSaveReplacementExpression f;
            DialogResult result;

            mSearchReplacement.Name = textBox_setName.Text;
            mSearchReplacement.SearchExpression = textBox_searchExpr.Text;
            mSearchReplacement.ReplaceExpression = textBox_replaceExpr.Text;
            mSearchReplacement.CaseSensitive = checkBox_caseSensitive.Checked;
            mSearchReplacement.UseRegex = checkBox_regex.Checked;

            f = new FormSaveReplacementExpression(mSearchReplacement, mSettings);

            result = f.ShowDialog(this);
            if (result == DialogResult.OK)
                mSearchReplacement = f.Replacement.Clone();
            f.Close();

            if (result != DialogResult.OK)
                return;

            textBox_setName.Text = mSearchReplacement.Name;
            textBox_searchExpr.Text = mSearchReplacement.SearchExpression;
            textBox_replaceExpr.Text = mSearchReplacement.ReplaceExpression;
            checkBox_regex.Checked = mSearchReplacement.UseRegex;
        }

        private void button_searchReplaceApply_Click(object sender, EventArgs e)
        {
            ApplySearchReplace();
        }

        /// <summary>
        /// Gets the current Search/replace settings, and goes to
        /// work on the specified grid data
        /// </summary>
        private void ApplySearchReplace() 
        {
            Replacement replacement;

            // No need to check for content as long as the textbox
            // prohibits the buttons when empty
            replacement = new Replacement();
            replacement.UseRegex = checkBox_regex.Checked;
            replacement.SearchExpression = textBox_searchExpr.Text;
            replacement.ReplaceExpression = textBox_replaceExpr.Text;
            replacement.CaseSensitive = checkBox_caseSensitive.Checked;

            SearchAndReplace(replacement, true);
        }

        private void textBox_searchExpr_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsSearchReplace();
        }

        private void SetTabControlsSearchReplace()
        {
            if (mColumnsSelected == 0)
            {
                button_searchReplaceApply.Enabled = false;
            }
            if (textBox_searchExpr.Text.Length > 0)
            {
                if (mColumnsSelected > 0)
                    button_searchReplaceApply.Enabled = true;

                button_searchReplaceSave.Enabled = true;
                if (tabControl1.SelectedTab.Text == "Search and Replace")
                {
                    if (textBox_testString.Text.Length > 0)
                        button_test.Enabled = true;
                    else
                        button_test.Enabled = true;
                }
            }
            else    // If no find expression
            {
                button_searchReplaceSave.Enabled = false;
                if (tabControl1.SelectedTab.Text == "Search and Replace")
                    button_test.Enabled = false;
            }
        }

        #endregion Search and Replace functionality

        #region Case functionality

        /// <summary>
        /// Does a case replacement on a grid row
        /// </summary>
        /// <param name="rowNum">Number of the grid row</param>
        /// <param name="columnIndices">Array of indexes of columns receiving action</param>
        /// <param name="searchExpression">Expression locating items for casing</param>
        /// <param name="toUpper">Whether to make located items upper-case</param>
        /// <param name="caseSensitive">Is the search case-sensitive?</param>
        private void GridRowApplyCase(int rowNum, int[] columnIndices, string searchExpression, bool toUpper, bool caseSensitive)
        {
            String columnValue, replacedValue;
            object cellValue;

            for (int j = 0; j < columnIndices.Length; j++)
            {
                cellValue = mGrid[columnIndices[j], rowNum].Value;
                if( cellValue == null )
                    return;
                columnValue = cellValue.ToString();
                try
                {
                    replacedValue = this.ReplaceCase(searchExpression, columnValue, toUpper, caseSensitive);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(this, ex.Message, "Replace case", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    throw ex;
                }
                if (replacedValue != columnValue)
                    mGrid[columnIndices[j], rowNum].Value = replacedValue;
            }
        }

        /// <summary>
        /// Sets upper or lower case according to criteria
        /// </summary>
        /// <param name="searchExpression">Regular expression identifying items to case</param>
        /// <param name="replaceText">Text on which to operate</param>
        /// <param name="toUpper">Whether to set to upper case (false=lower)</param>
        /// <param name="caseSensitive">Should the search be case-sensitive (where appropriate)</param>
        /// <returns></returns>
        private string ReplaceCase(string searchExpression, string replaceText, bool toUpper, bool caseSensitive)
        {
            Match regexMatch;
            int index, i;
            char[] newText;
            string returnValue;
            RegexOptions options;

            if (caseSensitive)
                options = RegexOptions.None;
            else
                options = RegexOptions.IgnoreCase;

            newText = replaceText.ToCharArray();
            try
            {
                regexMatch = Regex.Match(replaceText, searchExpression, options);
            }
            catch (Exception ex)
            {
                throw new Exception("Regular expression error: " + ex.Message);
            }
            while (regexMatch.Success)
            {
                // Since some matches, like word boundaries are zero-length, do at least once
                i = 0;
                do
                {
                    index = regexMatch.Index + i;
                    if (index >= newText.Length)
                        break;
                    if (toUpper)
                        newText[index] = char.ToUpper(replaceText[index]);
                    else
                        newText[index] = char.ToLower(replaceText[index]);
                } while (++i < regexMatch.Length);
                regexMatch = regexMatch.NextMatch();
            }
            returnValue = new string(newText);
            return returnValue;
        }

        private void button_caseTest_Click(object sender, EventArgs e)
        {
            TestCasing();
        }

        /// <summary>
        /// Tests an upper/lower casing scheme. Takes the "Test String" 
        /// item and fills in the "Result" item with the transformation.
        /// </summary>
        private void TestCasing()
        {
            bool toUpper, caseSensitive;

            if (textBox_caseExpression.Text.Length == 0)
            {
                MessageBox.Show(this, "You need a conversion expression",
                    "Test Case Conversion", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            if (textBox_testString.Text.Length == 0)
            {
                MessageBox.Show(this, "You need a test string",
                    "Test Case Conversion", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            toUpper = radioButton_upper.Checked;
            caseSensitive = checkBox_caseSensitiveCaseSearch.Checked;
            try
            {
                textBox_resultString.Text = ReplaceCase(textBox_caseExpression.Text,
                    textBox_testString.Text, toUpper, caseSensitive);
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "Replace case", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void button_caseApply_Click(object sender, EventArgs e)
        {
            Replacement replacement;

            if (textBox_caseExpression.Text.Length == 0)
            {
                MessageBox.Show(this, "You need a conversion expression",
                    "Case Conversion", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            replacement = new Replacement();
            replacement.SearchExpression = textBox_caseExpression.Text;
            if (radioButton_upper.Checked)
                replacement.ToUpper = true;
            else
                replacement.ToUpper = false;
            ApplyCasing(replacement);
        }

        /// <summary>
        /// Applies an upper/lower casing scheme to the grid
        /// </summary>
        /// <param name="replacement">Object with appropriate information</param>
        private void ApplyCasing(Replacement replacement)
        {
            int columnsChecked, currentRow;
            int[] columnIndices;
            string gridCaption;

            if ((columnsChecked = checkedListBox_columns.CheckedItems.Count) == 0)
            {
                MessageBox.Show(this, "No columns selected to work on.", "Casing", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            columnIndices = new int[columnsChecked];
            for (int i = 0; i < columnsChecked; i++)
            {
                gridCaption = checkedListBox_columns.CheckedItems[i].ToString();
                columnIndices[i] = mGridColumns[gridCaption].Index;
            }

            if (radioButton_all.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                {
                    try
                    {
                        GridRowApplyCase(i, columnIndices, replacement.SearchExpression, replacement.ToUpper, replacement.CaseSensitive);
                    }
                    catch
                    {
                        break;
                    }
                }
            }
            if (radioButton_current.Checked)
            {
                currentRow = mGrid.CurrentRow.Index;
                if (currentRow < 0)
                {
                    MessageBox.Show(this, "No row is selected.", 
                        "Casing", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }
                GridRowApplyCase(currentRow, columnIndices, replacement.SearchExpression, replacement.ToUpper, replacement.CaseSensitive);

            }
            if (radioButton_selected.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                {
                    if (mGrid.Rows[i].Selected)
                    {
                        try
                        {
                            GridRowApplyCase(i, columnIndices, replacement.SearchExpression, replacement.ToUpper, replacement.CaseSensitive);
                        }
                        catch
                        {
                            break;
                        }
                    }
                }
            }
        }

        private void textBox_caseExpression_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsCase();
        }

        private void SetTabControlsCase()
        {
            if (mColumnsSelected == 0)
            {
                button_caseApply.Enabled = false;
            }
            if (textBox_caseExpression.Text.Length > 0)
            {
                button_caseSave.Enabled = true;
                if (mColumnsSelected > 0)
                    button_caseApply.Enabled = true;
                if (tabControl1.SelectedTab.Text == "Upper/Lower Case")
                {
                    if (textBox_testString.Text.Length > 0)
                        button_test.Enabled = true;
                    else
                        button_test.Enabled = false;
                }
            }
            else
            {
                button_caseSave.Enabled = false;
                if (tabControl1.SelectedTab.Text == "Upper/Lower Case")
                    button_test.Enabled = false;
            }
        }

        private void button_caseLoad_Click(object sender, EventArgs e)
        {
            LoadCaseExpression();
        }

        /// <summary>
        /// Loads a case conversion expression from the database.
        /// </summary>
        private void LoadCaseExpression() 
        {
            FormLoadCaseExpression f;
            DialogResult result;

            f = new FormLoadCaseExpression(mSettings);
            result = f.ShowDialog(this);

            if (result == DialogResult.OK)
                mCaseReplacement = f.Replacement;
            f.Close();

            // Check the database to see whether the item was updated
            mCaseReplacement = Util.ReplacementFactory(mCaseReplacement.ID, mSettings);

            textBox_caseSetName.Text = mCaseReplacement.Name;
            textBox_caseExpression.Text = mCaseReplacement.SearchExpression;
            if (mCaseReplacement.ToUpper)
                radioButton_upper.Checked = true;
            else
                radioButton_lower.Checked = true;
            if (mCaseReplacement.CaseSensitive)
                checkBox_caseSensitiveCaseSearch.Checked = true;
            else
                checkBox_caseSensitiveCaseSearch.Checked = false;
        }

        private void button_caseSave_Click(object sender, EventArgs e)
        {
            SaveCaseExpression();
        }

        /// <summary>
        /// Saves the current case expression to the database
        /// </summary>
        private void SaveCaseExpression()
        {
            FormSaveCaseExpression f;
            DialogResult result;

            mCaseReplacement.Name = textBox_caseSetName.Text;
            mCaseReplacement.SearchExpression = textBox_caseExpression.Text;
            if (radioButton_upper.Checked)
                mCaseReplacement.ToUpper = true;
            else
                mCaseReplacement.ToUpper = false;
            if (checkBox_caseSensitiveCaseSearch.Checked)
                mCaseReplacement.CaseSensitive = true;
            else
                mCaseReplacement.CaseSensitive = false;

            f = new FormSaveCaseExpression(mCaseReplacement, mSettings);

            result = f.ShowDialog(this);
            if (result == DialogResult.OK)
                mCaseReplacement = f.Replacement.Clone();
            f.Close();

            if (result != DialogResult.OK)
                return;

            textBox_caseSetName.Text = mCaseReplacement.Name;
            textBox_caseExpression.Text = mCaseReplacement.SearchExpression;
            if (mCaseReplacement.ToUpper)
                radioButton_upper.Checked = true;
            else
                radioButton_lower.Checked = true;
        }

        #endregion Case functionality

        #region Split functionality

        private void button_splitApply_Click(object sender, EventArgs e)
        {
            SplitApply();
        }

        private void dataGridView_splitResult_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            SetTabControlsSplit();
        }

        private void textBox_splitExpression_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsSplit();
        }

        private void comboBox_splitSource_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsSplit();
        }

        private void SplitApply()
        {
            if (!GetSplitInfo(true))
                return;
            if (radioButton_all.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                {
                    GridRowSplit(i, mSplit);
                }
            }
            else if (radioButton_current.Checked)
                GridRowSplit(mGrid.CurrentCell.RowIndex, mSplit);
            else if (radioButton_selected.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                {
                    if (mGrid.Rows[i].Selected)
                        GridRowSplit(i, mSplit);
                }
            }
            // Make utterly certain there's a redraw
            mGrid.InvalidateRow(mGrid.CurrentRow.Index);
            mGrid.Invalidate();
        }

        /// <summary>
        /// Runs a test of the split functionality. Where the apply
        /// functionality trims the output, this shows the raw stuff
        /// </summary>
        private void TestSplit()
        {
            if (!GetSplitInfo(false))
                return;
            string sourceText, resultString;
            string[] splitResults;

            sourceText = textBox_testString.Text;
            splitResults = DoSplit(sourceText, mSplit.SplitExpression, mSplit.UseRegex);
            if (splitResults.Length == 0)
                resultString = "[no result]";
            else
            {
                if (splitResults.Length == 1)
                    resultString = "1 item: ";
                else
                    resultString = splitResults.Length.ToString() + " items: ";
                for (int i = 0; i < splitResults.Length; i++)
                    resultString += "[" + splitResults[i] + "]";
            }
            textBox_resultString.Text = resultString;
        }

        private bool GetSplitInfo(bool fullApply)
        {
            string sourceColumnHeading, resultColumnHeading;
            int orderNum, resultIndex;
            DataRowView drv;

            mSplit.Clear();

            mSplit.UseRegex = checkBox_splitRegex.Checked;
            mSplit.SplitExpression = textBox_splitExpression.Text;
            if (mSplit.SplitExpression.Length == 0)
            {
                MessageBox.Show(this, "Please fill in the split expression", "Split", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            if (!fullApply)
            {
                if (textBox_testString.Text == string.Empty)
                {
                    MessageBox.Show(this, "Please supply a test string", "Split", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return false;
                }

                // If testing, we have all the information we need at this point
                return true;
            }

            // This is what we need to apply the split
            sourceColumnHeading = comboBox_splitSource.Text;
            if (sourceColumnHeading.Length == 0)
            {
                MessageBox.Show(this, "Source column not chosen", "Split", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            mSplit.SourceColumnIndex = mGridColumns[sourceColumnHeading].Index;

            // Construct a dictionary: 
            //  key = array dimensions of the split.
            // Member items are the columns which receive that
            // piece of the split
            for (int i = 0; i < bindingSource_splitResult.Count; i++)
            {
                drv = (DataRowView)bindingSource_splitResult[i];
                if (drv["Order"] == DBNull.Value)
                    continue;
                orderNum = Convert.ToInt32(drv["Order"]);
                if (orderNum < 1)
                    continue;
                resultColumnHeading = drv["ColumnName"].ToString();
                resultIndex = mGridColumns[resultColumnHeading].Index;
                mSplit.AddResultMapItem(orderNum, resultIndex);
            }
            if (mSplit.ResultMap.Count == 0)
            {
                MessageBox.Show(this, "Result columns not chosen", "Split", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return false;
            }
            return true;
        }

        private void GridRowSplit(int rowNum, Split split)
        {
            string sourceText, replaceText;
            string[] splitResults;
            object cellValue;

            cellValue = mGrid[split.SourceColumnIndex, rowNum].Value;
            // We could hit an added row
            if (cellValue == null)
                return;
            sourceText = cellValue.ToString();
            splitResults = DoSplit(sourceText, split.SplitExpression, split.UseRegex);
            //sourceText.Split(split.SplitExpression.ToCharArray(), StringSplitOptions.None);
            for (int i = 0; i < splitResults.Length; i++)
            {
                replaceText = splitResults[i].Trim();
                // If this part of the split isn't mentioned, skip
                // (could blow up if not a valid key)
                if (!split.ResultMap.ContainsKey(i))
                    continue;
                for (int j = 0; j < split.ResultMap[i].Count; j++)
                    mGrid[split.ResultMap[i][j], rowNum].Value = replaceText;
            }
        }

        private string[] DoSplit(string sourceText, string splitExpression, bool useRegex)
        {
            string[] splitResults = null;

            if (!useRegex)
                splitResults = sourceText.Split(splitExpression.ToCharArray(), StringSplitOptions.None);
            else
                splitResults = Regex.Split(sourceText, splitExpression);

            return splitResults;
        }

        private void SetTabControlsSplit()
        {
            object resultValue;
            bool resultAssigned;

            if (textBox_splitExpression.Text.Length > 0)
            {
                if (tabControl1.SelectedTab.Text == "Split")
                {
                    if (textBox_testString.Text.Length > 0)
                        button_test.Enabled = true;
                    else
                        button_test.Enabled = false;
                }
                resultAssigned = false;
                if (comboBox_splitSource.Text.Length > 0)
                {
                    for (int i = 0; i < dataGridView_splitResult.RowCount; i++)
                    {
                        resultValue = dataGridView_splitResult[orderDataGridViewTextBoxColumn.Index, i].Value;
                        if (resultValue == DBNull.Value)
                            continue;
                        try
                        {
                            // We've prohibited text entry, but
                            // it never hurts to try/catch
                            if (Convert.ToInt32(resultValue) > 0)
                            {
                                resultAssigned = true;
                                break;
                            }
                        }
                        catch
                        {
                            continue;
                        }
                    }
                    if (resultAssigned)
                    {
                        button_splitApply.Enabled = true;
                        button_splitSave.Enabled = true;
                    }
                    else
                    {
                        button_splitApply.Enabled = false;
                        button_splitSave.Enabled = false;
                    }
                }
            }
            else        // no split expression
            {
                button_splitApply.Enabled = false;
                button_splitSave.Enabled = false;
                if (tabControl1.SelectedTab.Text == "Split")
                    button_test.Enabled = false;
            }
        }

        private void button_splitLoad_Click(object sender, EventArgs e)
        {
            FormLoadSplitExpression f;
            DialogResult result;
            int nameIndex, splitIndex;

            f = new FormLoadSplitExpression(mGridColumns, mSettings);
            result = f.ShowDialog(this);

            if (result == DialogResult.OK)
                mSplit = f.Split;
            f.Close();

            // If missing (deleted), the display will be blanked with empty values
            textBox_splitExpression.Text = mSplit.SearchExpression;
            checkBox_splitRegex.Checked = mSplit.UseRegex;
            comboBox_splitSource.Text = mSplit.SourceColumnName;
            textBox_splitName.Text = mSplit.Name;

            nameIndex = columnNameDataGridViewTextBoxColumn.Index;
            splitIndex = orderDataGridViewTextBoxColumn.Index;

            // Get rid of any existing indices
            for (int i = 0; i < dataGridView_splitResult.RowCount; i++)
                dataGridView_splitResult[splitIndex, i].Value = DBNull.Value;

            // Put in current indices
            for (int i = 0; i < mSplit.Columns.Count; i++)
            {
                for (int j = 0; j < dataGridView_splitResult.RowCount; j++)
                {
                    if (dataGridView_splitResult[nameIndex, j].Value.ToString() == mSplit.Columns[i].ColumnName)
                        dataGridView_splitResult[splitIndex, j].Value = mSplit.Columns[i].SplitNum;
                }
            }
        }

        private void button_splitSave_Click(object sender, EventArgs e)
        {
            SaveSplitItem();
        }

        private void SaveSplitItem()
        {
            FormSaveSplitExpression f;
            DialogResult result;
            int splitNum;
            int nameIndex, splitIndex;
            GridColumn newColumn;

            mSplit.Name = textBox_splitName.Text;
            mSplit.SearchExpression = textBox_splitExpression.Text;
            mSplit.SourceColumnName = comboBox_splitSource.Text;
            mSplit.UseRegex = checkBox_splitRegex.Checked;
            mSplit.Columns.Clear();

            nameIndex = columnNameDataGridViewTextBoxColumn.Index;
            splitIndex = orderDataGridViewTextBoxColumn.Index;

            for (int i = 0; i < dataGridView_splitResult.RowCount; i++)
            {
                splitNum = -1;
                if (dataGridView_splitResult[splitIndex, i].Value != DBNull.Value)
                    splitNum = Convert.ToInt32(dataGridView_splitResult[splitIndex, i].Value);
                if (splitNum > 0)
                {
                    newColumn = new GridColumn();
                    newColumn.SplitNum = splitNum;
                    newColumn.ColumnName = dataGridView_splitResult[nameIndex, i].Value.ToString();
                    mSplit.Columns.Add(newColumn);
                }
            }

            f = new FormSaveSplitExpression(mSplit, mSettings);

            result = f.ShowDialog(this);
            if (result == DialogResult.OK)
                mSplit = f.Split.Clone();
            f.Close();

            if (result != DialogResult.OK)
                return;

            textBox_splitName.Text = mSplit.Name;
            textBox_splitExpression.Text = mSplit.SearchExpression;
            comboBox_splitSource.Text = mSplit.SourceColumnName;
            checkBox_splitRegex.Checked = mSplit.UseRegex;
        }

        #endregion Split functionality
        
        #region Macro functionality

        private void button_macroEdit_Click(object sender, EventArgs e)
        {
            EditMacro();
        }

        private void EditMacro() 
        {
            FormEditMacro f;
            DialogResult result;

            f = new FormEditMacro(GetCurrentMacro(), mSettings);

            result = f.ShowDialog();
            f.Close();

            if (result != DialogResult.OK)
                return;
            SetTabControlsMacro();
            // Refresh list if edited
            dataSet_macro1.Tables["Macro"].Clear();
            oleDbDataAdapter_macro.Fill(dataSet_macro1, "Macro");
        }

        private Macro GetCurrentMacro() 
        {
            DataRowView drv;
            Macro returnMacro = new Macro();

            if (bindingSource_macro.Count == 0)
                return returnMacro;
            drv = (DataRowView)bindingSource_macro.Current;
            returnMacro.mMacroID = Convert.ToInt32(drv["MacroID"]);
            returnMacro.Name = drv["Name"].ToString();
            returnMacro.Description = drv["Description"].ToString();

            return returnMacro;
        }

        private void dataGridView_macro_DoubleClick(object sender, EventArgs e)
        {
            EditMacro();
        }

        private void TestMacro(Macro macro) 
        {
            List<GridProcess> processList;
            string testExpression;
            Replacement replacement;
            int casing, typeSearchAndReplace;

            casing = Util.GetGridProcessTypeID("Casing", mSettings);
            typeSearchAndReplace = Util.GetGridProcessTypeID("SearchAndReplace", mSettings);

            if (macro.ID < 0)
            {
                MessageBox.Show(this, "No macro is defined",
                    "Test Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            testExpression = textBox_testString.Text;
            if (testExpression.Length == 0)
            {
                MessageBox.Show(this, "You need a test expression",
                    "Test Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            processList = Util.RetrieveMacroProcesses(macro.ID, mGridColumns, mSettings);
            for (int i = 0; i < processList.Count; i++)
            { 
                if (processList[i].GridProcessTypeID == casing)
                {
                    replacement = (Replacement)processList[i];
                    try
                    {
                        testExpression = ReplaceCase(replacement.SearchExpression,
                            testExpression, replacement.ToUpper, replacement.CaseSensitive);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(this, ex.Message, "Replace case: " + replacement.Name, MessageBoxButtons.OK, MessageBoxIcon.Error);
                        break;
                    }
                }
                else if (processList[i].GridProcessTypeID == typeSearchAndReplace)
                {
                    replacement = (Replacement)processList[i];
                    if (replacement.UseRegex)
                        testExpression = Util.ReplaceRegex(testExpression,
                            replacement.SearchExpression, replacement.ReplaceExpression, replacement.CaseSensitive);
                    else
                        testExpression = Util.ReplaceLiteral(testExpression,
                            replacement.SearchExpression, replacement.ReplaceExpression, replacement.CaseSensitive);
                }
            }
            textBox_resultString.Text = testExpression;
        }

        /// <summary>
        /// Compiles command list, checks row scope,
        /// and hands each command off for execution
        /// </summary>
        /// <param name="macro">A macro object</param>
        private void ApplyMacro(Macro macro)
        {
            int columnsChecked;
            string[] columnNames;
            int[] columnIndices;
            string gridCaption;

            List<GridProcess> processList;

            if (macro.ID < 0)
            {
                MessageBox.Show(this, "No macro is defined",
                    "Test Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            if ((columnsChecked = checkedListBox_columns.CheckedItems.Count) == 0)
            {
                MessageBox.Show(this, "No columns selected to work on.", 
                    "Apply Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            columnIndices = new int[columnsChecked];
            columnNames = new string[columnsChecked];
            for (int i = 0; i < columnsChecked; i++)
            {
                gridCaption = checkedListBox_columns.CheckedItems[i].ToString();
                columnIndices[i] = mGridColumns[gridCaption].Index;
            }

            processList = Util.RetrieveMacroProcesses(macro.ID, mGridColumns, mSettings);
            if (radioButton_all.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                    GridRowApplyMacro(processList, i, columnIndices, columnNames);
            }
            else if (radioButton_current.Checked)
                GridRowApplyMacro(processList, mGrid.CurrentRow.Index, columnIndices, columnNames);
            else if (radioButton_selected.Checked)
            {
                for (int i = 0; i < mGrid.Rows.Count; i++)
                {
                    if (mGrid.Rows[i].Selected)
                        GridRowApplyMacro(processList, i, columnIndices, columnNames);
                }
            }
        }

        /// <summary>
        /// Applies a macro to a particular row. A dispatcher function,
        /// it checks the type, casts the process variable, and calls 
        /// the proper GridRowxxx() function to do its job.
        /// </summary>
        /// <param name="processList">The processes of the macro</param>
        /// <param name="rowNum">The row to work on</param>
        /// <param name="columnIndices">The indices columns chosen by the user</param>
        /// <param name="columnNames">The names of the columns chosen by the user</param>
        private void GridRowApplyMacro(List<GridProcess> processList, int rowNum, int [] columnIndices, string [] columnNames)
        {
            int typeCasing, typeSearchAndReplace, typeSplit;
            Replacement replacement;
            Split split;

            // A makeshift enumeration for readability
            typeCasing = Util.GetGridProcessTypeID("Casing", mSettings);
            typeSearchAndReplace = Util.GetGridProcessTypeID("SearchAndReplace", mSettings);
            typeSplit = Util.GetGridProcessTypeID("Split", mSettings);

            for (int i = 0; i < processList.Count; i++)
            {
                if (processList[i].GridProcessTypeID == typeCasing)
                {
                    replacement = (Replacement)processList[i];
                    GridRowApplyCase(rowNum, columnIndices, replacement.SearchExpression, replacement.ToUpper, replacement.CaseSensitive);
                }
                else if (processList[i].GridProcessTypeID == typeSearchAndReplace)
                {
                    replacement = (Replacement)processList[i];
                    GridRowSearchAndReplace(rowNum, columnIndices, replacement.SearchExpression, replacement.ReplaceExpression, replacement.UseRegex, replacement.CaseSensitive);
                }
                else if (processList[i].GridProcessTypeID == typeSplit)
                {
                    split = (Split)processList[i];
                    GridRowSplit(rowNum, split);
                }
            }
        }

        private void button_macroApply_Click(object sender, EventArgs e)
        {
            ApplyMacro(GetCurrentMacro());
        }

        private void renameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            RenameMacro();
        }

        private void RenameMacro()
        {
            FormNewName f;
            DialogResult result;
            Macro currentMacro;
            string newName = string.Empty;
            DataRowView drv;
            int foundIndex;

            currentMacro = GetCurrentMacro();
            f = new FormNewName(currentMacro.Name);
            result = f.ShowDialog(this);
            if (result == DialogResult.OK)
                newName = f.NewName;
            f.Close();
            if (result != DialogResult.OK)
                return;

            // A bit of validation
            if (newName.Length == 0)
            {
                MessageBox.Show(this, "The new name can't be blank.",
                    "Rename Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            if (currentMacro.Name == newName)
            {
                MessageBox.Show(this, "The new name is the same as the old one.",
                    "Rename Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            foundIndex = bindingSource_macro.Find("Name", newName);
            if( foundIndex >= 0 )
            {
                MessageBox.Show(this, "The new name already exists.",
                    "Rename Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            // Current item in the grid
            drv = (DataRowView)bindingSource_macro.Current;
            drv["Name"] = newName;
            // Flush to underlying source
            bindingSource_macro.EndEdit();
            try
            {
                oleDbDataAdapter_macro.Update(dataSet_macro1, "Macro");
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, "Update error: " + ex.Message,
                    "Rename Macro", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
            dataGridView_macro.Invalidate();
        }

        private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DeleteMacro();
        }

        private void DeleteMacro() 
        {
            DialogResult result;

            result = MessageBox.Show(this, "Delete macro?", "Delete macro",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (result != DialogResult.Yes)
                return;
            bindingSource_macro.RemoveCurrent();
            try
            {
                oleDbDataAdapter_macro.Update(dataSet_macro1, "Macro");
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, "Delete error: " + ex.Message,
                    "Delete Macro", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void editToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EditMacro();
        }

        private void contextMenuStrip_macro_Opening(object sender, CancelEventArgs e)
        {
            if (bindingSource_macro.Count == 0)
            {
                renameToolStripMenuItem.Enabled = false;
                editToolStripMenuItem.Enabled = false;
                deleteToolStripMenuItem.Enabled = false;
            }
            else
            {
                renameToolStripMenuItem.Enabled = true;
                editToolStripMenuItem.Enabled = true;
                deleteToolStripMenuItem.Enabled = true;
            }
        }

        private void RefreshMacroGrid() 
        {
            dataSet_macro1.Clear();
            oleDbDataAdapter_macro.Fill(dataSet_macro1, "Macro");
        }

        private void dataGridView_macro_MouseDown(object sender, MouseEventArgs e)
        {
            Util.GridMouseDown(dataGridView_macro, sender, e);
        }

        private void button_macroRename_Click(object sender, EventArgs e)
        {
            RenameMacro();
        }

        private void button_macroDelete_Click(object sender, EventArgs e)
        {
            DeleteMacro();
        }

        private void SetTabControlsMacro()
        {
            if (dataGridView_macro.RowCount == 0)
            {
                button_macroApply.Enabled = false;
                if (tabControl1.SelectedTab.Text == "Macros")
                    button_test.Enabled = false;
            }
            else
            {
                if (tabControl1.SelectedTab.Text == "Macros")
                    button_test.Enabled = true;
                if (mColumnsSelected == 0)
                    button_macroApply.Enabled = false;
                else
                    button_macroApply.Enabled = true;
            }
        }

        #endregion Macro functionality

        #region Search column functionality (checked listbox)

        /// <summary>
        /// Checks (or unchecks) all columns for actions
        /// </summary>
        /// <param name="setValue"></param>
        private void SetAllColumns(bool setValue)
        {
            for (int i = 0; i < checkedListBox_columns.Items.Count; i++)
                checkedListBox_columns.SetItemChecked(i, setValue);
        }

        // Toggles the selection of all columns (or none)
        private void button_checkAll_Click(object sender, EventArgs e)
        {
            bool hasCheck = false;

            for (int i = 0; i < checkedListBox_columns.Items.Count; i++)
            {
                if (checkedListBox_columns.GetItemChecked(i))
                {
                    hasCheck = true;
                    break;
                }
            }
            SetAllColumns(!hasCheck);
        }

        private void checkedListBox_columns_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (e.NewValue == CheckState.Unchecked)
                mColumnsSelected = checkedListBox_columns.CheckedItems.Count - 1;

            else if (e.NewValue == CheckState.Checked)
                mColumnsSelected = checkedListBox_columns.CheckedItems.Count + 1;

            SetTabControlsCurrent();
        }

        #endregion Search column functionality (checked listbox)

        #region Testing functionality

        private void button_test_Click(object sender, EventArgs e)
        {
            string tabText;

            tabText = tabControl1.SelectedTab.Text;
            switch (tabText)
            {
                case "Search and Replace": TestReplace(); break;
                case "Upper/Lower Case": TestCasing(); break;
                case "Macros": TestMacro(GetCurrentMacro()); break;
                case "Split": TestSplit(); break;
                case "Find": TestFind(); break;
            }
        }

        private void textBox_testString_TextChanged(object sender, EventArgs e)
        {
            SetTabControlsCurrent();
        }

        #endregion Testing functionality

        #region Form Properties

        /// <summary>
        /// The current database settings
        /// </summary>
        public Settings Settings
        {
            get { return mSettings; }
        }

        /// <summary>
        /// The grid on which this form operates
        /// </summary>
        public DataGridView Grid
        {
            get { return mGrid; }
        }

        #endregion Form Properties

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United Kingdom United Kingdom
I'm a London-based software developer. Originally from New York, I came here in 1997 to run European application development for Standard & Poors. I now work independently ... and I'm still here!

Having seen how US software behaves outside the US, I'm keenly interested in problems of global and multilingual software design. I also used to write intelligence-gathering software, and still can't resist a well-turned algorithm! Before my IT career I was in music, and I'm now combining both interests in a highly-exciting 'Project-X'. I could tell you what it is, but ....

When not thinking about all of the above, I'm fascinated by all aspects of different cultures. (You can't take New York out of the New Yorker.) Interests include jazz, classical and world music, languages, history and ethnic food. I'm also an amateur travel writer and photographer, and run a site at www.travelogues.net, which you're welcome to stop by and visit!

Comments and Discussions