using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.Runtime.InteropServices;
using System.IO;
namespace RegexFindAndReplace
{
public partial class FindAndReplaceForm : Form
{
/// <summary>
/// The CaptureInfo struct is used to hold information about a captured
/// sub-expression in the regex when building the "Captured sub-expressions"
/// menu item
/// </summary>
struct CaptureInfo
{
public int Start;
public int Length;
public string Name;
public int Number;
public CaptureInfo( int start, int length, string name, int number )
{
Start = start;
Length = length;
Name = name;
Number = number;
}
}
private DTE2 applicationObject;
private FinderAndReplacer finderAndReplacer;
private MultipleFinderAndReplacer multipleFinderAndReplacer;
private List<CaptureInfo> captureInfoList;
private Dictionary<string, CaptureInfo> captureInfoHash;
private int regexSelectionStart;
private int regexSelectionLength;
private SettingsCache settingsCache;
/// <summary>
/// Initializes a new instance of the FindAndReplaceForm class.
/// </summary>
private FindAndReplaceForm()
{
InitializeComponent();
this.captureInfoList = new List<CaptureInfo>();
this.captureInfoHash = new Dictionary<string, CaptureInfo>();
this.settingsCache = new SettingsCache();
// add the handler that highlights captured sub-expressions in the regex
for ( int i = 0; i < this.regexHelpContextMenuStrip.Items.Count; i++ )
{
this.regexHelpContextMenuStrip.Items[ i ].MouseEnter += new EventHandler( this.HandleHelpContextMenuStripItemMouseEnter );
}
for ( int i = 0; i < this.replacementHelpContextMenuStrip.Items.Count; i++ )
{
this.replacementHelpContextMenuStrip.Items[ i ].MouseEnter += new EventHandler( this.HandleHelpContextMenuStripItemMouseEnter );
}
}
/// <summary>
/// Initializes a new instance of the FindAndReplaceForm class.
/// </summary>
/// <param name="applicationObject">The application object.</param>
public FindAndReplaceForm( DTE2 applicationObject )
: this()
{
this.applicationObject = applicationObject;
this.finderAndReplacer = new FinderAndReplacer( this.applicationObject );
this.multipleFinderAndReplacer = new MultipleFinderAndReplacer( this.applicationObject );
int textDocumentCount = 0;
// determine if the current document is a text document
if ( this.applicationObject.ActiveDocument != null && this.applicationObject.ActiveDocument.Object( string.Empty ) is TextDocument )
{
// add the current document option
this.lookInComboBox.Items.Add( Strings.CURRENT_DOCUMENT );
}
// determine if any text documents are open
foreach ( Document document in this.applicationObject.Documents )
{
if ( document.Object( string.Empty ) is TextDocument )
{
if ( ++textDocumentCount > 1 )
{
break;
}
}
}
// if there is more than one text documents open
if ( textDocumentCount > 0 )
{
if ( textDocumentCount > 1 )
{
// add the all open documents option
this.lookInComboBox.Items.Add( Strings.ALL_OPEN_DOCUMENTS );
}
}
// if a solution has been opened
if ( this.applicationObject.Solution.IsOpen )
{
// add the current project option
this.lookInComboBox.Items.Add( Strings.CURRENT_PROJECT );
// if there is more than one project
if ( this.applicationObject.Solution.Projects.Count > 1 )
{
// add the entire solution option
this.lookInComboBox.Items.Add( Strings.ENTIRE_SOLUTION );
}
}
// determine if text is selected in the current document
if ( this.finderAndReplacer.GetTextSelection() != null && !this.finderAndReplacer.GetTextSelection().IsEmpty )
{
// add the selection option.
int position = this.lookInComboBox.Items.Count > 0 ? 1 : 0;
this.lookInComboBox.Items.Insert( position, Strings.SELECTION );
this.lookInComboBox.SelectedIndex = position;
}
else if ( this.lookInComboBox.Items.Count > 0 )
{
this.lookInComboBox.SelectedIndex = 0;
}
else
{
// disable the buttons if nothing is available to search
this.findNextButton.Enabled = false;
this.replaceButton.Enabled = false;
this.replaceAllButton.Enabled = false;
this.skipFileButton.Enabled = false;
}
try
{
// try to get the text editor's font
Properties textEditorProperties = this.applicationObject.get_Properties( "FontsAndColors", "TextEditor" );
Property textEditorFontFamily = textEditorProperties.Item( "FontFamily" );
Property textEditorFontSize = textEditorProperties.Item( "FontSize" );
// set the font of the pattern editors
this.regexComboBox.Font = this.regexTextBox.Font = this.replacementComboBox.Font = this.replacementTextBox.Font =
new Font( textEditorFontFamily.Value.ToString(), float.Parse( textEditorFontSize.Value.ToString() ) );
}
catch
{
// use a generic momospace font on failure
this.regexComboBox.Font = this.regexTextBox.Font = this.replacementComboBox.Font = this.replacementTextBox.Font =
new Font( FontFamily.GenericMonospace, 8.25f );
}
Stream bitmapStream = null;
try
{
// try to set the arrow image on the pattern help buttons
bitmapStream = this.GetType().Assembly.GetManifestResourceStream( "RegexFindAndReplace.arrow.bmp" );
if ( bitmapStream != null )
{
Bitmap arrowBitmap = new Bitmap( bitmapStream );
arrowBitmap.MakeTransparent( Color.White );
this.regexHelpButton.Image = arrowBitmap;
this.replacementHelpButton.Image = arrowBitmap;
}
}
catch
{
bitmapStream = null;
}
finally
{
if ( bitmapStream == null )
{
this.regexHelpButton.Text = ">";
this.replacementHelpButton.Text = ">";
}
}
}
/// <summary>
/// Gets settings that were cached in the registry
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleFindAndReplaceFormLoad( object sender, EventArgs e )
{
this.findAndReplaceTypeButton.Text = this.settingsCache.GetCachedSetting( Strings.Settings.DIALOG_TYPE, Strings.REGEX_FIND_AND_REPLACE );
UpdateOptionsFromCache( this.regexComboBox, PatternType.Regex );
UpdateOptionsFromCache( this.replacementComboBox, PatternType.Replacement );
this.regexTextBox.Text = this.regexComboBox.Text =
this.settingsCache.GetCachedSetting( Strings.Settings.SELECTED_REGEX, string.Empty );
this.replacementTextBox.Text = this.replacementComboBox.Text =
this.settingsCache.GetCachedSetting( Strings.Settings.SELECTED_REPLACEMENT, string.Empty );
string cachedLookIn = this.settingsCache.GetCachedSetting( Strings.Settings.SELECTED_LOOK_IN, Strings.CURRENT_PROJECT );
// set the look in field if it is a valid option or if it is a directory
if ( this.lookInComboBox.Items.Contains( cachedLookIn ) || ( Directory.Exists( cachedLookIn ) && this.findAndReplaceTypeButton.Text != Strings.REGEX_FIND_AND_REPLACE ) )
{
this.lookInComboBox.Text = cachedLookIn;
// enable the buttons
this.findNextButton.Enabled = true;
this.replaceButton.Enabled = true;
this.replaceAllButton.Enabled = true;
this.skipFileButton.Enabled = true;
}
this.includeSubDirectoriesCheckBox.Checked = bool.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.INCLUDE_SUB_DIRECTORIES, "true" ) );
SetRegexOptions( (RegexOptions)int.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.REGEX_OPTIONS, "0" ) ) );
this.fileTypesComboBox.Text = this.settingsCache.GetCachedSetting( Strings.Settings.SELECTED_FILE_TYPES, "*.*" );
this.displayFileNamesCheckBox.Checked = bool.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.DISPLAY_FILE_NAMES_ONLY, "false" ) );
this.keepFilesOpenCheckBox.Checked = bool.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.KEEP_MODIFIED_FILES_OPEN, "false" ) );
this.matchContextBeforeNumericUpDown.Value = decimal.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.MATCH_CONTEXT_BEFORE_LINE_COUNT, "0" ) );
this.matchContextAfterNumericUpDown.Value = decimal.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.MATCH_CONTEXT_AFTER_LINE_COUNT, "0" ) );
UpdateConditionallyOptionalControls();
}
/// <summary>
/// Caches settings on the dialog
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.FormClosedEventArgs"/> instance containing the event data.</param>
private void HandleFindAndReplaceFormFormClosed( object sender, FormClosedEventArgs e )
{
this.settingsCache.CacheSetting( Strings.Settings.DIALOG_TYPE, this.findAndReplaceTypeButton.Text );
if ( this.regexComboBox.SelectedIndex != -1 && this.regexComboBox.Text != string.Empty )
{
this.settingsCache.CacheSetting( Strings.Settings.SELECTED_REGEX, this.regexTextBox.Text );
}
if ( this.replacementComboBox.SelectedIndex != -1 && this.replacementComboBox.Text != string.Empty )
{
this.settingsCache.CacheSetting( Strings.Settings.SELECTED_REPLACEMENT, this.replacementTextBox.Text );
}
if ( this.lookInComboBox.Text != string.Empty )
{
this.settingsCache.CacheSetting( Strings.Settings.SELECTED_LOOK_IN, this.lookInComboBox.Text );
}
this.settingsCache.CacheSetting( Strings.Settings.INCLUDE_SUB_DIRECTORIES, this.includeSubDirectoriesCheckBox.Checked.ToString() );
this.settingsCache.CacheSetting( Strings.Settings.REGEX_OPTIONS, ( (int)GetRegexOptions() ).ToString() );
if ( this.fileTypesComboBox.Text != string.Empty )
{
this.settingsCache.CacheSetting( Strings.Settings.SELECTED_FILE_TYPES, this.fileTypesComboBox.Text );
}
this.settingsCache.CacheSetting( Strings.Settings.DISPLAY_FILE_NAMES_ONLY, this.displayFileNamesCheckBox.Checked.ToString() );
this.settingsCache.CacheSetting( Strings.Settings.KEEP_MODIFIED_FILES_OPEN, this.keepFilesOpenCheckBox.Checked.ToString() );
this.settingsCache.CacheSetting( Strings.Settings.MATCH_CONTEXT_BEFORE_LINE_COUNT, this.matchContextBeforeNumericUpDown.Value.ToString() );
this.settingsCache.CacheSetting( Strings.Settings.MATCH_CONTEXT_AFTER_LINE_COUNT, this.matchContextAfterNumericUpDown.Value.ToString() );
}
/// <summary>
/// Selects the text in the regex text box when it receives focus
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleRegexTextBoxEnter( object sender, EventArgs e )
{
this.regexTextBox.Select( 0, this.regexTextBox.TextLength );
}
/// <summary>
/// Emulates common combobox keyboard operations when the regex text box has focus
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
private void HandleRegexTextBoxKeyDown( object sender, KeyEventArgs e )
{
if ( e.KeyCode == Keys.Up )
{
if ( this.regexTextBox.GetLineFromCharIndex( this.regexTextBox.SelectionStart ) == 0 )
{
if ( this.regexComboBox.SelectedIndex > 0 )
{
--this.regexComboBox.SelectedIndex;
}
}
}
else if ( e.KeyCode == Keys.Down )
{
if ( ( e.Modifiers & Keys.Alt ) == Keys.Alt )
{
this.regexComboBox.DroppedDown = true;
}
else
{
if ( this.regexTextBox.GetLineFromCharIndex( this.regexTextBox.SelectionStart ) == this.regexTextBox.Lines.Length - 1 )
{
if ( this.regexComboBox.SelectedIndex < this.regexComboBox.Items.Count - 1 )
{
++this.regexComboBox.SelectedIndex;
}
}
}
}
}
/// <summary>
/// Sets the text of the regex text box when the regex combo box selection changes
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleRegexComboBoxSelectedIndexChanged( object sender, EventArgs e )
{
this.regexTextBox.Text = this.regexComboBox.Text;
this.regexTextBox.Focus();
this.regexTextBox.Select( 0, this.regexTextBox.TextLength );
}
/// <summary>
/// Selects the text in the replacement text box when it receives focus
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleReplacementTextBoxEnter( object sender, EventArgs e )
{
this.replacementTextBox.Select( 0, this.replacementTextBox.TextLength );
}
/// <summary>
/// Emulates common combobox keyboard operations when the replacement text box has focus
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
private void HandleReplacementTextBoxKeyDown( object sender, KeyEventArgs e )
{
if ( e.KeyCode == Keys.Up )
{
if ( this.replacementTextBox.GetLineFromCharIndex( this.replacementTextBox.SelectionStart ) == 0 )
{
if ( this.replacementComboBox.SelectedIndex > 0 )
{
--this.replacementComboBox.SelectedIndex;
}
}
}
else if ( e.KeyCode == Keys.Down )
{
if ( ( e.Modifiers & Keys.Alt ) == Keys.Alt )
{
this.replacementComboBox.DroppedDown = true;
}
else
{
if ( this.replacementTextBox.GetLineFromCharIndex( this.replacementTextBox.SelectionStart ) == this.replacementTextBox.Lines.Length - 1 )
{
if ( this.replacementComboBox.SelectedIndex < this.replacementComboBox.Items.Count - 1 )
{
++this.replacementComboBox.SelectedIndex;
}
}
}
}
}
/// <summary>
/// Sets the text of the replacement text box when the replacement combo box selection changes
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleReplacementComboBoxSelectedIndexChanged( object sender, EventArgs e )
{
this.replacementTextBox.Text = this.replacementComboBox.Text;
this.replacementTextBox.Focus();
this.replacementTextBox.Select( 0, this.replacementTextBox.TextLength );
}
/// <summary>
/// Called by the owner-drawn pattern comboboxes to determin the height
/// and width of the available options
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.MeasureItemEventArgs"/> instance containing the event data.</param>
private void HandleComboBoxMeasureItem( object sender, MeasureItemEventArgs e )
{
ComboBox comboBox = (ComboBox)sender;
SizeF optionSize = e.Graphics.MeasureString( comboBox.Items[ e.Index ].ToString(), comboBox.Font );
e.ItemWidth = (int)optionSize.Width;
e.ItemHeight = (int)optionSize.Height;
// increase the width of the combobox to avoid showing a scrollbar
if ( e.ItemWidth > comboBox.DropDownWidth )
{
comboBox.DropDownWidth = e.ItemWidth;
}
}
/// <summary>
/// Called by the owner-drawn pattern comboboxes to draw the options in the dropped-down menu
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.DrawItemEventArgs"/> instance containing the event data.</param>
private void HandleComboBoxDrawItem( object sender, DrawItemEventArgs e )
{
ComboBox comboBox = (ComboBox)sender;
if ( e.State == ( DrawItemState.Selected | DrawItemState.NoAccelerator | DrawItemState.NoFocusRect ) || e.State == DrawItemState.Selected )
{
e.Graphics.FillRectangle(
new SolidBrush( SystemColors.Highlight ),
new Rectangle( e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height ) );
e.Graphics.DrawString(
comboBox.Items[ e.Index ].ToString(),
comboBox.Font,
new SolidBrush( SystemColors.HighlightText ),
new Rectangle( e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height ),
StringFormat.GenericDefault );
}
else
{
e.Graphics.FillRectangle(
new SolidBrush( Color.White ),
new Rectangle( e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height ) );
e.Graphics.DrawString(
comboBox.Items[ e.Index ].ToString(),
comboBox.Font,
new SolidBrush( Color.Black ),
new Rectangle( e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height ),
StringFormat.GenericDefault );
}
}
/// <summary>
/// Displays the regex help context menu
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleRegexHelpButtonClick( object sender, EventArgs e )
{
this.regexSelectionStart = this.regexTextBox.SelectionStart;
this.regexSelectionLength = this.regexTextBox.SelectionLength;
// check the regex for sub-expressions
FindCapturedSubExpressions();
while ( this.regexHelpContextMenuStrip.Items.Count > 18 )
{
this.regexHelpContextMenuStrip.Items.RemoveAt( 18 );
}
if ( this.captureInfoList.Count > 0 )
{
this.captureInfoHash.Clear();
ToolStripMenuItem subExpressionsMenuItem = new ToolStripMenuItem( "Captured sub-expressions" );
// create a menu item for each sub-expression in the regex
for ( int i = 0; i < this.captureInfoList.Count; i++ )
{
string menuItemText = string.Empty;
if ( this.captureInfoList[ i ].Name != null )
{
menuItemText = string.Format( @"\k<{0}>", this.captureInfoList[ i ].Name );
}
else
{
menuItemText = string.Format( @"\{0}", this.captureInfoList[ i ].Number );
}
subExpressionsMenuItem.DropDownItems.Add( menuItemText );
subExpressionsMenuItem.DropDownItems[ subExpressionsMenuItem.DropDownItems.Count - 1 ].MouseEnter += new EventHandler( HandleHelpContextMenuStripItemMouseEnter );
subExpressionsMenuItem.DropDownItems[ subExpressionsMenuItem.DropDownItems.Count - 1 ].Click += new EventHandler( HandleRegexSubExpressionItemClicked );
this.captureInfoHash[ menuItemText ] = this.captureInfoList[ i ];
}
this.regexHelpContextMenuStrip.Items.Add( new ToolStripSeparator() );
this.regexHelpContextMenuStrip.Items.Add( subExpressionsMenuItem );
}
this.regexHelpContextMenuStrip.Show( this, new Point( this.regexHelpButton.Right, this.regexHelpButton.Top ) );
}
/// <summary>
/// Redirects a click event to HandleRegexHelpContextMenuStripItemClicked
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void HandleRegexSubExpressionItemClicked( object sender, EventArgs e )
{
HandleRegexHelpContextMenuStripItemClicked( sender, new ToolStripItemClickedEventArgs( (ToolStripItem)sender ) );
}
/// <summary>
/// Called when a menu item in the regex help context menu is clicked. Inserts a regex expression into the current regex
/// at the current cursor position
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.ToolStripItemClickedEventArgs"/> instance containing the event data.</param>
private void HandleRegexHelpContextMenuStripItemClicked( object sender, ToolStripItemClickedEventArgs e )
{
int selectionStart = this.regexSelectionStart;
string genericNestedRegex = @"<Start>(?>[^<Start><End>]+|<Start>(?<Depth>)|<End>(?<-Depth>))*(?(Depth)(?!))<End>";
string genericNestedRegexWithWhiteSpace =
@"<Start>
(?>
[^<Start><End>]+
|
<Start>(?<Depth>)
|
<End>(?<-Depth>)
)*
(?(Depth)(?!))
<End>";
this.regexTextBox.SelectionStart = 0;
this.regexTextBox.SelectionLength = this.regexTextBox.Text.Length;
this.regexTextBox.SelectionColor = Color.Black;
this.regexTextBox.SelectionStart = this.regexSelectionStart;
this.regexTextBox.SelectionLength = this.regexSelectionLength;
if ( e.ClickedItem == anySingleCharacterToolStripMenuItem )
{
this.regexTextBox.SelectedText = ".";
selectionStart += 1;
}
else if ( e.ClickedItem == zeroOrMoreToolStripMenuItem )
{
this.regexTextBox.SelectedText = "*";
selectionStart += 1;
}
else if ( e.ClickedItem == oneOrMoreToolStripMenuItem )
{
this.regexTextBox.SelectedText = "+";
selectionStart += 1;
}
else if ( e.ClickedItem == alternationToolStripMenuItem )
{
this.regexTextBox.SelectedText = "|";
selectionStart += 1;
}
else if ( e.ClickedItem == characterClassToolStripMenuItem )
{
this.regexTextBox.SelectedText = "[]";
selectionStart += 1;
}
else if ( e.ClickedItem == negatedCharacterClassToolStripMenuItem )
{
this.regexTextBox.SelectedText = "[^]";
selectionStart += 2;
}
else if ( e.ClickedItem == beginningOfLineToolStripMenuItem )
{
this.regexTextBox.SelectedText = "^";
selectionStart += 1;
}
else if ( e.ClickedItem == endOfLineToolStripMenuItem )
{
this.regexTextBox.SelectedText = "$";
selectionStart += 1;
}
else if ( e.ClickedItem == wordBoundryToolStripMenuItem )
{
this.regexTextBox.SelectedText = @"\b";
selectionStart += 2;
}
else if ( e.ClickedItem == positiveLookaheadToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?=)";
selectionStart += 3;
}
else if ( e.ClickedItem == negativeLookaheadToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?!)";
selectionStart += 3;
}
else if ( e.ClickedItem == positiveLookbehindToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?<=)";
selectionStart += 4;
}
else if ( e.ClickedItem == negativeLookbehindToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?<!)";
selectionStart += 4;
}
else if ( e.ClickedItem == nameNamedCaptureToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?<Name>)";
selectionStart += 8;
}
else if ( e.ClickedItem == groupingOnlyParenthesesToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?:)";
selectionStart += 3;
}
else if ( e.ClickedItem == atomicGroupingToolStripMenuItem )
{
this.regexTextBox.SelectedText = "(?>)";
selectionStart += 3;
}
else if ( e.ClickedItem == nestedBracesToolStripMenuItem )
{
string nestedBraceRegex = string.Empty;
if ( this.ignoreWhitespaceCheckBox.Checked )
{
nestedBraceRegex = genericNestedRegexWithWhiteSpace.Replace( "<Start>", "{" ).Replace( "<End>", "}" );
}
else
{
nestedBraceRegex = genericNestedRegex.Replace( "<Start>", "{" ).Replace( "<End>", "}" );
}
this.regexTextBox.SelectedText = nestedBraceRegex;
selectionStart += nestedBraceRegex.Length;
}
else if ( e.ClickedItem == nestedParenthesesToolStripMenuItem )
{
string nestedParenthesesRegex = string.Empty;
if ( this.ignoreWhitespaceCheckBox.Checked )
{
nestedParenthesesRegex = genericNestedRegexWithWhiteSpace.Replace( "<Start>", @"\(" ).Replace( "<End>", @"\)" );
}
else
{
nestedParenthesesRegex = genericNestedRegex.Replace( "<Start>", @"\(" ).Replace( "<End>", @"\)" );
}
this.regexTextBox.SelectedText = nestedParenthesesRegex;
selectionStart += nestedParenthesesRegex.Length;
}
else
{
this.regexTextBox.SelectedText = e.ClickedItem.Text;
selectionStart += e.ClickedItem.Text.Length;
}
this.regexTextBox.Focus();
this.regexSelectionStart = this.regexTextBox.SelectionStart = selectionStart;
}
/// <summary>
/// Displays the replacement help context menu
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleReplacementHelpButtonClick( object sender, EventArgs e )
{
this.regexSelectionStart = this.regexTextBox.SelectionStart;
this.regexSelectionLength = this.regexTextBox.SelectionLength;
// check the regex for sub-expressions
FindCapturedSubExpressions();
while ( this.replacementHelpContextMenuStrip.Items.Count > 3 )
{
this.replacementHelpContextMenuStrip.Items.RemoveAt( 3 );
}
if ( this.captureInfoList.Count > 0 )
{
this.captureInfoHash.Clear();
ToolStripMenuItem subExpressionsMenuItem = new ToolStripMenuItem( "Captured sub-expressions" );
// create a menu item for each captured sub-expression
for ( int i = 0; i < this.captureInfoList.Count; i++ )
{
string menuItemText = string.Empty;
if ( this.captureInfoList[ i ].Name != null )
{
menuItemText = string.Format( @"${{{0}}}", this.captureInfoList[ i ].Name );
}
else
{
menuItemText = string.Format( @"${0}", this.captureInfoList[ i ].Number );
}
subExpressionsMenuItem.DropDownItems.Add( menuItemText );
subExpressionsMenuItem.DropDownItems[ subExpressionsMenuItem.DropDownItems.Count - 1 ].MouseEnter += new EventHandler( HandleHelpContextMenuStripItemMouseEnter );
subExpressionsMenuItem.DropDownItems[ subExpressionsMenuItem.DropDownItems.Count - 1 ].Click += new EventHandler( HandleReplacementSubExpressionItemClicked );
this.captureInfoHash[ menuItemText ] = this.captureInfoList[ i ];
}
this.replacementHelpContextMenuStrip.Items.Add( new ToolStripSeparator() );
this.replacementHelpContextMenuStrip.Items.Add( subExpressionsMenuItem );
}
this.replacementHelpContextMenuStrip.Show( this, new Point( replacementHelpButton.Right, replacementHelpButton.Top ) );
}
/// <summary>
/// Redirects a click event to HandleReplacementHelpContextMenuStripItemClicked
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
void HandleReplacementSubExpressionItemClicked( object sender, EventArgs e )
{
HandleReplacementHelpContextMenuStripItemClicked( sender, new ToolStripItemClickedEventArgs( (ToolStripItem)sender ) );
}
/// <summary>
/// Called when a menu item in the replacement help context menu is clicked. Inserts a replacement expression into the current replacement
/// at the current cursor position
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.ToolStripItemClickedEventArgs"/> instance containing the event data.</param>
private void HandleReplacementHelpContextMenuStripItemClicked( object sender, ToolStripItemClickedEventArgs e )
{
int selectionStart = this.replacementTextBox.SelectionStart;
if ( e.ClickedItem == matchedTextToolStripMenuItem )
{
this.replacementTextBox.SelectedText = "$&";
selectionStart += 2;
}
else if ( e.ClickedItem == textBeforeMatchToolStripMenuItem )
{
this.replacementTextBox.SelectedText = "$`";
selectionStart += 2;
}
else if ( e.ClickedItem == textAfterMatchToolStripMenuItem )
{
this.replacementTextBox.SelectedText = "$'";
selectionStart += 2;
}
else
{
this.replacementTextBox.SelectedText = e.ClickedItem.Text;
selectionStart += e.ClickedItem.Text.Length;
}
this.replacementTextBox.Focus();
this.replacementTextBox.SelectionStart = selectionStart;
this.regexTextBox.SelectionStart = 0;
this.regexTextBox.SelectionLength = this.regexTextBox.Text.Length;
this.regexTextBox.SelectionColor = Color.Black;
}
/// <summary>
/// Parses the current regex for captured sub-expressions and stores information about each expression for the
/// creation of menu items
/// </summary>
private void FindCapturedSubExpressions()
{
Regex balancedParenthesesRegex = new Regex( @"^\((?!\?:)(\?<(?<Name>\w+)>)?(?>[^()]+|\((?<Depth>)|\)(?<-Depth>))*(?(Depth)(?!))\)" );
int iCapturedSubExpressionCount = 1;
while ( this.replacementHelpContextMenuStrip.Items.Count > 3 )
{
this.replacementHelpContextMenuStrip.Items.RemoveAt( 3 );
}
this.captureInfoList.Clear();
for ( int i = 0; i < this.regexTextBox.Text.Length; i++ )
{
int escapeCount = 0;
for ( int j = i; j > 0; j-- )
{
if ( this.regexTextBox.Text[ j ] == '\\' )
{
++escapeCount;
}
else
{
break;
}
}
if ( ( escapeCount & 1 ) == 1 )
{
continue;
}
Match balancedParenthesesMatch = balancedParenthesesRegex.Match( this.regexTextBox.Text.Substring( i ) );
if ( balancedParenthesesMatch.Success )
{
string menuItemText = string.Empty;
if ( balancedParenthesesMatch.Groups[ "Name" ].Value != string.Empty )
{
this.captureInfoList.Add( new CaptureInfo( i, balancedParenthesesMatch.Length, balancedParenthesesMatch.Groups[ "Name" ].Value, -1 ) );
}
else if ( !this.explicitCaptureCheckBox.Checked )
{
this.captureInfoList.Add( new CaptureInfo( i, balancedParenthesesMatch.Length, null, iCapturedSubExpressionCount++ ) );
}
}
}
}
/// <summary>
/// Called when the mouse enters a menu item in the regex and replacement context menus. If the item is for
/// a captured sub-expression, that expression is highlighted in blue in the regex.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleHelpContextMenuStripItemMouseEnter( object sender, EventArgs e )
{
this.regexTextBox.SelectionStart = 0;
this.regexTextBox.SelectionLength = this.regexTextBox.Text.Length;
this.regexTextBox.SelectionColor = Color.Black;
if ( this.captureInfoHash.ContainsKey( ( sender as ToolStripItem ).Text ) )
{
CaptureInfo captureInfo = this.captureInfoHash[ ( sender as ToolStripItem ).Text ];
this.regexTextBox.SelectionStart = captureInfo.Start;
this.regexTextBox.SelectionLength = captureInfo.Length;
this.regexTextBox.SelectionColor = Color.Blue;
}
}
/// <summary>
/// Clears any highlighted text in the regex and reset the selection
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.Windows.Forms.ToolStripDropDownClosingEventArgs"/> instance containing the event data.</param>
private void HelpContextMenuStripClosing( object sender, ToolStripDropDownClosingEventArgs e )
{
this.regexTextBox.SelectionStart = 0;
this.regexTextBox.SelectionLength = this.regexTextBox.Text.Length;
this.regexTextBox.SelectionColor = Color.Black;
this.regexTextBox.SelectionStart = this.regexSelectionStart;
this.regexTextBox.SelectionLength = this.regexSelectionLength;
}
/// <summary>
/// Called when the dialog type is changed
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleFindAndReplaceTypeMenuItemClick( object sender, EventArgs e )
{
this.findAndReplaceTypeButton.Text = ( (ToolStripMenuItem)sender ).Text;
}
/// <summary>
/// Called when the type of the dialog is changed using the findAndReplaceTypeButton.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleFindAndReplaceTypeButtonTextChanged( object sender, EventArgs e )
{
UpdateControlPositions();
}
/// <summary>
/// Displays or hides controls depending on the type of the dialog, and the state of the group boxes
/// </summary>
private void UpdateControlPositions()
{
if ( this.findAndReplaceTypeButton.Text == Strings.REGEX_FIND_AND_REPLACE )
{
this.lookInComboBox.Width = this.lookInBrowseButton.Right - this.lookInComboBox.Left;
this.includeSubDirectoriesPanel.Visible = false;
this.standardButtonsPanel.Visible = true;
this.skipFileButton.Visible = true;
this.fileTypesPanel.Visible = false;
this.resultOptionsPanel.Visible = false;
this.findAllButtonPanel.Visible = false;
this.regexOptionsPanel.Top = this.lookInComboBox.Bottom;
this.standardButtonsPanel.Top = this.regexOptionsPanel.Bottom;
this.ClientSize = new Size( this.ClientSize.Width, this.standardButtonsPanel.Bottom );
}
else if ( this.findAndReplaceTypeButton.Text == Strings.REGEX_FIND_IN_FILES )
{
this.lookInComboBox.Width = this.regexComboBox.Width;
this.includeSubDirectoriesPanel.Visible = true;
this.standardButtonsPanel.Visible = false;
this.skipFileButton.Visible = false;
this.fileTypesPanel.Visible = true;
this.resultOptionsPanel.Visible = true;
this.findAllButtonPanel.Visible = true;
this.includeSubDirectoriesPanel.Top = this.lookInComboBox.Bottom;
this.regexOptionsPanel.Top = this.includeSubDirectoriesPanel.Bottom;
this.findAllButtonPanel.Top = this.resultOptionsPanel.Bottom;
this.ClientSize = new Size( this.ClientSize.Width, this.findAllButtonPanel.Bottom );
UpdateConditionallyOptionalControls();
}
else
{
this.lookInComboBox.Width = this.regexComboBox.Width;
this.standardButtonsPanel.Visible = true;
this.skipFileButton.Visible = true;
this.fileTypesPanel.Visible = true;
this.resultOptionsPanel.Visible = true;
this.findAllButtonPanel.Visible = false;
this.includeSubDirectoriesPanel.Top = this.lookInComboBox.Bottom;
this.regexOptionsPanel.Top = this.includeSubDirectoriesPanel.Bottom;
this.standardButtonsPanel.Top = this.resultOptionsPanel.Bottom;
this.ClientSize = new Size( this.ClientSize.Width, this.standardButtonsPanel.Bottom );
UpdateConditionallyOptionalControls();
}
}
/// <summary>
/// Displays a FolderBrowserDialog in a find or replace in files type dialog, so a directory can be chosen for a search.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleLookInBrowseButtonClick( object sender, EventArgs e )
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if ( Directory.Exists( this.lookInComboBox.Text ) )
{
folderBrowserDialog.SelectedPath = this.lookInComboBox.Text;
}
else
{
if ( this.applicationObject.Solution.FullName != string.Empty )
{
folderBrowserDialog.SelectedPath = Path.GetDirectoryName( this.applicationObject.Solution.FullName );
}
else
{
Document document = null;
if ( ( document = (Document)this.applicationObject.ActiveDocument ) != null )
{
folderBrowserDialog.SelectedPath = Path.GetDirectoryName( document.FullName );
}
}
}
folderBrowserDialog.Description = "Choose a directory to search.";
if ( folderBrowserDialog.ShowDialog() == DialogResult.OK )
{
this.lookInComboBox.Text = folderBrowserDialog.SelectedPath;
UpdateConditionallyOptionalControls();
}
}
/// <summary>
/// Called when the "Find Next" button is clicked.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleFindNextButtonClick( object sender, EventArgs e )
{
// validate the regex and RegexOptions
if ( ValidateRegexAndOptions() )
{
try
{
// update the cache.
CachePatterns();
if ( this.lookInComboBox.Text == Strings.CURRENT_DOCUMENT )
{
this.finderAndReplacer.FindNext();
}
else
{
UpdateItemsToProcess();
if ( this.findAndReplaceTypeButton.Text != Strings.REGEX_FIND_AND_REPLACE )
{
this.multipleFinderAndReplacer.FileTypes = this.fileTypesComboBox.Text;
this.multipleFinderAndReplacer.IncludeSubDirectories = this.includeSubDirectoriesCheckBox.Checked;
}
this.multipleFinderAndReplacer.FindNext();
}
}
catch ( Exception ex )
{
MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
}
finally
{
UpdateOptionsFromCache( this.regexComboBox, PatternType.Regex );
}
}
}
/// <summary>
/// Called when the "Find All" button is clicked.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleFindAllButtonClick( object sender, EventArgs e )
{
// validate the regex and RegexOptions
if ( ValidateRegexAndOptions() )
{
try
{
// update the cache
CachePatterns();
this.multipleFinderAndReplacer.FileTypes = this.fileTypesComboBox.Text;
this.multipleFinderAndReplacer.DisplayOnlyFileNames = this.displayFileNamesCheckBox.Checked;
this.multipleFinderAndReplacer.IncludeSubDirectories = this.includeSubDirectoriesCheckBox.Checked;
this.multipleFinderAndReplacer.MatchContextBeforeLineCount = (int)this.matchContextBeforeNumericUpDown.Value;
this.multipleFinderAndReplacer.MatchContextAfterLineCount = (int)this.matchContextAfterNumericUpDown.Value;
this.multipleFinderAndReplacer.ContinueProcessing = true;
this.stopButton.Enabled = this.stopButton.Visible = true;
foreach ( Control control in this.Controls )
{
if ( control != this.stopButton )
{
control.Enabled = false;
}
}
this.multipleFinderAndReplacer.FindAll( GetProcessAllScope() );
foreach ( Control control in this.Controls )
{
control.Enabled = true;
}
this.stopButton.Enabled = this.stopButton.Visible = false;
this.applicationObject.DTE.Windows.Item( Constants.vsWindowKindOutput ).Visible = true;
}
catch ( Exception ex )
{
MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
}
finally
{
UpdateOptionsFromCache( this.regexComboBox, PatternType.Regex );
}
}
}
/// <summary>
/// Called when the "Replace" button is clicked
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleReplaceButtonClick( object sender, EventArgs e )
{
// validate the regex and RegexOptions
if ( ValidateRegexAndOptions() )
{
try
{
// update the cache
CachePatterns();
if ( this.lookInComboBox.Text == Strings.CURRENT_DOCUMENT )
{
this.finderAndReplacer.ReplaceNext( this.replacementTextBox.Text );
}
else
{
UpdateItemsToProcess();
this.multipleFinderAndReplacer.ReplaceNext( this.replacementTextBox.Text );
}
}
catch ( Exception ex )
{
MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
}
finally
{
UpdateOptionsFromCache( this.replacementComboBox, PatternType.Replacement );
}
}
}
/// <summary>
/// Called when the "Replace All" button is clicked
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleReplaceAllButtonClick( object sender, EventArgs e )
{
// validate the regex and RegexOptions
if ( ValidateRegexAndOptions() )
{
try
{
// update the cache
CachePatterns();
this.multipleFinderAndReplacer.FileTypes = this.fileTypesComboBox.Text;
this.multipleFinderAndReplacer.DisplayOnlyFileNames = this.displayFileNamesCheckBox.Checked;
this.multipleFinderAndReplacer.IncludeSubDirectories = this.includeSubDirectoriesCheckBox.Checked;
this.multipleFinderAndReplacer.MatchContextBeforeLineCount = (int)this.matchContextBeforeNumericUpDown.Value;
this.multipleFinderAndReplacer.MatchContextAfterLineCount = (int)this.matchContextAfterNumericUpDown.Value;
this.multipleFinderAndReplacer.KeepModifedFilesOpen = this.keepFilesOpenCheckBox.Checked;
this.multipleFinderAndReplacer.ContinueProcessing = true;
this.stopButton.Enabled = this.stopButton.Visible = true;
foreach ( Control control in this.Controls )
{
if ( control != this.stopButton )
{
control.Enabled = false;
}
}
this.multipleFinderAndReplacer.ReplaceAll( GetProcessAllScope(), this.replacementTextBox.Text, this.findAndReplaceTypeButton.Text == Strings.REGEX_REPLACE_IN_FILES );
foreach ( Control control in this.Controls )
{
control.Enabled = true;
}
this.stopButton.Enabled = this.stopButton.Visible = false;
this.applicationObject.DTE.Windows.Item( Constants.vsWindowKindOutput ).Visible = true;
}
catch ( Exception ex )
{
MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
}
finally
{
UpdateOptionsFromCache( this.replacementComboBox, PatternType.Replacement );
}
}
}
/// <summary>
/// Gets the scope of a find and replace operation based on the contents of the "Look in" combo box
/// </summary>
/// <returns></returns>
private object GetProcessAllScope()
{
object findAllScope = string.Empty;
switch ( this.lookInComboBox.Text )
{
case Strings.CURRENT_DOCUMENT:
findAllScope = this.applicationObject.ActiveDocument.FullName;
break;
case Strings.ALL_OPEN_DOCUMENTS:
findAllScope = Strings.ALL_OPEN_DOCUMENTS;
break;
case Strings.CURRENT_PROJECT:
findAllScope = (Project)( (Array)this.applicationObject.ActiveSolutionProjects ).GetValue( 0 );
break;
case Strings.ENTIRE_SOLUTION:
findAllScope = Strings.ENTIRE_SOLUTION;
break;
default:
if ( Directory.Exists( this.lookInComboBox.Text ) )
{
findAllScope = this.lookInComboBox.Text;
}
else
{
finderAndReplacer.ShowMessage( string.Format( "The specified directory does not exist: {0}", this.lookInComboBox.Text ), true );
}
break;
}
return findAllScope;
}
/// <summary>
/// Called when the "Stop" button is clicked. Stops a Find all or Replace all operation.
/// </summary>
private void HandleStopButtonClick( object sender, EventArgs e )
{
this.multipleFinderAndReplacer.ContinueProcessing = false;
}
/// <summary>
/// Called when the "Skip File" button is clicked.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleSkipFileButtonClick( object sender, EventArgs e )
{
if ( this.multipleFinderAndReplacer.ItemsToProcess.Count > 0 )
{
this.multipleFinderAndReplacer.ItemsToProcess.Pop();
}
else
{
UpdateItemsToProcess();
}
this.multipleFinderAndReplacer.FindNext();
}
/// <summary>
/// Validates the regex and RegexOptions. The FinderAndReplacer.Pattern and MultipleFinderAndReplacer.Pattern properties
/// attempt to create a new Regex and throw an exception upon failure
/// </summary>
/// <returns></returns>
private bool ValidateRegexAndOptions()
{
bool regexAndOptionsValid = true;
try
{
this.finderAndReplacer.Pattern = this.regexTextBox.Text;
this.finderAndReplacer.RegexOptions = GetRegexOptions();
this.multipleFinderAndReplacer.Pattern = this.regexTextBox.Text;
this.multipleFinderAndReplacer.RegexOptions = GetRegexOptions();
}
catch
{
regexAndOptionsValid = false;
}
return regexAndOptionsValid;
}
/// <summary>
/// Caches the current selections in the regex and replacement combo boxes, and moves them to the top of the list
/// </summary>
private void CachePatterns()
{
if ( !this.settingsCache.IsPatternCached( PatternType.Regex, this.regexTextBox.Text ) )
{
this.settingsCache.AddPattern( this.regexTextBox.Text, PatternType.Regex );
}
else
{
this.settingsCache.MovePatternToTop( this.regexTextBox.Text, PatternType.Regex );
}
if ( !this.settingsCache.IsPatternCached( PatternType.Replacement, this.replacementTextBox.Text ) )
{
this.settingsCache.AddPattern( this.replacementTextBox.Text, PatternType.Replacement );
}
else
{
this.settingsCache.MovePatternToTop( this.replacementTextBox.Text, PatternType.Replacement );
}
}
/// <summary>
/// Updates the items that will be processed by the MultipleFinderAndReplacer.
/// </summary>
private void UpdateItemsToProcess()
{
if ( this.multipleFinderAndReplacer.ItemsToProcess.Count == 0 )
{
List<string> filenames = new List<string>();
// the MultipleFinderAndReplacer.ItemsToProcess property is a stack where the top always contains
// the current item to process. If that item is a directory, the MultipleFinderAndReplacer removes
// it from the top of the stack and adds the files in that directory to the stack. If the item is
// a EnvDTE.Project, the text files in that project will be added to the stack.
try
{
switch ( this.lookInComboBox.Text )
{
case Strings.ALL_OPEN_DOCUMENTS:
foreach ( Document document in this.applicationObject.Documents )
{
if ( document.Object( string.Empty ) is TextDocument )
{
filenames.Add( document.FullName );
}
}
filenames.Sort();
// if the currently open file is in the list of file names, adjust the list so it will be
// processed first
if ( filenames.Contains( this.applicationObject.ActiveDocument.FullName ) )
{
int currentFileIndex = filenames.IndexOf( this.applicationObject.ActiveDocument.FullName );
List<string> currentFileAndAfters = filenames.GetRange( currentFileIndex, filenames.Count - currentFileIndex );
filenames.RemoveRange( currentFileIndex, filenames.Count - currentFileIndex );
filenames.InsertRange( 0, currentFileAndAfters );
}
// reverse the list
filenames.Reverse();
foreach ( string filename in filenames )
{
this.multipleFinderAndReplacer.ItemsToProcess.Push( filename );
}
break;
case Strings.CURRENT_PROJECT:
{
Project project = (Project)( (Array)this.applicationObject.ActiveSolutionProjects ).GetValue( 0 );
this.multipleFinderAndReplacer.ItemsToProcess.Push( project );
}
break;
case Strings.ENTIRE_SOLUTION:
List<Project> projects = new List<Project>();
foreach ( Project project in this.applicationObject.Solution.Projects )
{
projects.Add( project );
}
projects.Sort( new ProjectComparer() );
if ( ( (Array)this.applicationObject.ActiveSolutionProjects ).Length > 0 )
{
Project activeProject = (Project)( (Array)this.applicationObject.ActiveSolutionProjects ).GetValue( 0 );
// if the currently active project is in the list of projects, adjust the list so it will be
// processed first
if ( activeProject != null && projects.Contains( activeProject ) )
{
int currentProjectIndex = projects.IndexOf( activeProject );
List<Project> currentProjectAndAfters = projects.GetRange( currentProjectIndex, projects.Count - currentProjectIndex );
projects.RemoveRange( currentProjectIndex, projects.Count - currentProjectIndex );
projects.InsertRange( 0, currentProjectAndAfters );
}
}
projects.Reverse();
foreach ( Project project in projects )
{
this.multipleFinderAndReplacer.ItemsToProcess.Push( project );
}
break;
default:
if ( Directory.Exists( this.lookInComboBox.Text ) )
{
this.multipleFinderAndReplacer.ItemsToProcess.Push( this.lookInComboBox.Text );
}
break;
}
}
catch ( Exception ex )
{
MessageBox.Show( ex.Message, "Error!" );
}
}
}
/// <summary>
/// Gets the RegexOptions based on the state of the RegexOptions checkboxes
/// </summary>
/// <returns></returns>
private RegexOptions GetRegexOptions()
{
RegexOptions regexOptions = RegexOptions.None;
if ( this.ignoreCaseCheckBox.Checked )
{
regexOptions |= RegexOptions.IgnoreCase;
}
if ( this.multilineCheckBox.Checked )
{
regexOptions |= RegexOptions.Multiline;
}
if ( this.singlelineCheckBox.Checked )
{
regexOptions |= RegexOptions.Singleline;
}
if ( this.explicitCaptureCheckBox.Checked )
{
regexOptions |= RegexOptions.ExplicitCapture;
}
if ( this.ignoreWhitespaceCheckBox.Checked )
{
regexOptions |= RegexOptions.IgnorePatternWhitespace;
}
if ( this.ecmaScriptCheckBox.Checked )
{
regexOptions |= RegexOptions.ECMAScript;
}
if ( this.rightToLeftCheckBox.Checked )
{
regexOptions |= RegexOptions.RightToLeft;
}
if ( this.cultureInvariantCheckBox.Checked )
{
regexOptions |= RegexOptions.CultureInvariant;
}
return regexOptions;
}
/// <summary>
/// Updates the RegexOptions checkboxes with the specified RegexOptions
/// </summary>
/// <param name="regexOptions">The regex options.</param>
private void SetRegexOptions( RegexOptions regexOptions )
{
this.ignoreCaseCheckBox.Checked = ( regexOptions & RegexOptions.IgnoreCase ) == RegexOptions.IgnoreCase;
this.multilineCheckBox.Checked = ( regexOptions & RegexOptions.Multiline ) == RegexOptions.Multiline;
this.singlelineCheckBox.Checked = ( regexOptions & RegexOptions.Singleline ) == RegexOptions.Singleline;
this.explicitCaptureCheckBox.Checked = ( regexOptions & RegexOptions.ExplicitCapture ) == RegexOptions.ExplicitCapture;
this.ignoreWhitespaceCheckBox.Checked = ( regexOptions & RegexOptions.IgnorePatternWhitespace ) == RegexOptions.IgnorePatternWhitespace;
this.ecmaScriptCheckBox.Checked = ( regexOptions & RegexOptions.ECMAScript ) == RegexOptions.ECMAScript;
this.rightToLeftCheckBox.Checked = ( regexOptions & RegexOptions.RightToLeft ) == RegexOptions.RightToLeft;
this.cultureInvariantCheckBox.Checked = ( regexOptions & RegexOptions.CultureInvariant ) == RegexOptions.CultureInvariant;
}
/// <summary>
/// Updates the options in the specifed combobox with those that were cached
/// </summary>
/// <param name="comboBox">The combo box.</param>
/// <param name="regexType">Type of the regex.</param>
private void UpdateOptionsFromCache( ComboBox comboBox, PatternType regexType )
{
List<string> cachedOptionList = this.settingsCache.GetCachedPatterns( regexType );
comboBox.Items.Clear();
for ( int i = cachedOptionList.Count - 1; i >= 0; i-- )
{
comboBox.Items.Add( cachedOptionList[ i ] );
}
}
/// <summary>
/// Called when the selection in the lookInCombox changes. Enables or disables the appropriate buttons and controls
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleLookInComboBoxSelectedIndexChanged( object sender, EventArgs e )
{
if ( this.lookInComboBox.Text == Strings.SELECTION )
{
this.findNextButton.Enabled = false;
this.replaceButton.Enabled = false;
}
else
{
this.findNextButton.Enabled = true;
this.replaceButton.Enabled = true;
}
UpdateConditionallyOptionalControls();
// clear the multipleFinderAndReplacer.ItemsToProcess property
this.multipleFinderAndReplacer.ItemsToProcess.Clear();
}
/// <summary>
/// Clears the multipleFinderAndReplacer.ItemsToProcess property when the lookInComboBox text changes
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleLookInComboBoxTextChanged( object sender, EventArgs e )
{
this.multipleFinderAndReplacer.ItemsToProcess.Clear();
UpdateConditionallyOptionalControls();
}
/// <summary>
/// Updates the conditionally optional controls.
/// </summary>
private void UpdateConditionallyOptionalControls()
{
if ( this.findAndReplaceTypeButton.Text != Strings.REGEX_FIND_AND_REPLACE )
{
if ( Directory.Exists( this.lookInComboBox.Text ) )
{
this.fileTypesComboBox.Enabled = true;
this.fileTypesPanel.Visible = true;
this.includeSubDirectoriesCheckBox.Checked = bool.Parse( this.settingsCache.GetCachedSetting( Strings.Settings.INCLUDE_SUB_DIRECTORIES, "true" ) );
this.includeSubDirectoriesCheckBox.Enabled = true;
this.regexOptionsPanel.Top = this.includeSubDirectoriesPanel.Bottom;
this.resultOptionsPanel.Top = this.fileTypesPanel.Bottom;
this.standardButtonsPanel.Top = this.resultOptionsPanel.Bottom;
}
else
{
this.fileTypesComboBox.Enabled = false;
this.fileTypesPanel.Visible = false;
this.settingsCache.CacheSetting( Strings.Settings.INCLUDE_SUB_DIRECTORIES, this.includeSubDirectoriesCheckBox.Checked.ToString() );
this.includeSubDirectoriesCheckBox.Checked = true;
this.includeSubDirectoriesCheckBox.Enabled = false;
this.regexOptionsPanel.Top = this.lookInComboBox.Bottom;
this.resultOptionsPanel.Top = this.regexOptionsPanel.Bottom;
this.standardButtonsPanel.Top = this.resultOptionsPanel.Bottom;
}
if ( this.findAndReplaceTypeButton.Text == Strings.REGEX_FIND_IN_FILES )
{
this.keepFilesOpenCheckBox.Enabled = false;
this.findAllButtonPanel.Top = this.resultOptionsPanel.Bottom;
this.ClientSize = new Size( this.ClientSize.Width, this.findAllButtonPanel.Bottom );
}
else
{
this.keepFilesOpenCheckBox.Enabled = true;
this.findAllButtonPanel.Top = this.standardButtonsPanel.Bottom;
this.ClientSize = new Size( this.ClientSize.Width, this.standardButtonsPanel.Bottom );
}
}
else
{
this.ClientSize = new Size( this.ClientSize.Width, this.standardButtonsPanel.Bottom );
}
this.stopButton.Top = this.ClientSize.Height - this.stopButton.Height - 5;
this.stopButton.Enabled = this.stopButton.Visible = false;
}
/// <summary>
/// Called when the excape key is clicked
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
private void HandleCloseButtonClick( object sender, EventArgs e )
{
this.Close();
}
private void HandleRegexOptionsGroupBoxCollapseBoxClicked( object sender )
{
if ( this.regexOptionsGroupBox.IsCollapsed )
{
this.regexOptionsPanel.Height = this.regexOptionsGroupBox.CollapsedHeight + 4;
}
else
{
this.regexOptionsPanel.Height = this.regexOptionsGroupBox.FullHeight + 4;
}
UpdateControlPositions();
}
private void HandleResultOptionsGroupBoxCollapseBoxClicked( object sender )
{
if ( this.resultOptionsGroupBox.IsCollapsed )
{
this.resultOptionsPanel.Height = this.resultOptionsGroupBox.CollapsedHeight + 4;
}
else
{
this.resultOptionsPanel.Height = this.resultOptionsGroupBox.FullHeight + 4;
}
UpdateControlPositions();
}
}
}