You are here:About>Computing & Technology>Delphi Programming
About.comDelphi Programming
Adding Regular Expression Filename Filtering to ShellListView
How to add regular expression (filename) filtering to the Delphi file explorer application.
 More of this Feature
• Part 1: Shell Controls: Delphi's Hidden Gems
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Monitoring Shell Changes
• Windows API

Article submitted by: George Merriman for the Delphi Programming Quickies Contest - Round #1 Winner!

One of the nice things about Unix-style command line shells and utilities is the pervasive use of regular expressions for file name filtering. Much of this tradition has filtered into the Windows world with open source utilities such as Perl and Ruby, but it is rare in native GUI applications.
In this article, I show how to add regular expression filtering to the Delphi file explorer application I discussed last time.

   Regular Expressions
The key to this project is the regular expression pattern matching filter. Unfortunately, Delphi Pro does not provide any regular expression support out of the box. It does provide the MatchesMask function in the Masks unit, but this routine does not provide true regular expression matching we are interested in right now. I could have implemented my own: Kernighan and Plauger describe a Pascal implementation in their book Software Tools in Pascal. Fortunately, I was saved this trouble because the excellent SysTools library from TurboPower is now available as freeware on SourceForge.

GUI
Below is a screen shot of the enhanced Delphi test harness from last time. I removed the ShellComboBox and dropped a few simple controls onto the bottom panel so that I could exercise my regular expression filter. he two check boxes determine whether or not folders and hidden files are displayed, and the three radio buttons determine the filtering mode. The edit control is used to enter the filter pattern, and the Refresh button is used to refresh the list after a new pattern is entered. I also dropped a non-visible StRegEx component onto the form from the SysTools palette.

Enhanced Delphi file explorer

Code
So much for the preliminaries-now it's time for the good stuff. As with so many things in this business, the actual coding was dead simple, once I figured out how things worked. By poking around in ShellCtrls.pas, I discovered that the appropriate way do the filtering is to handle the list view OnAddFolder event. This confusingly named event is fired for each folder and file encountered in the directory specified by the associated tree view whenever the list view refreshes. The AFolder parameter is an object that describes a number of properties for each potential item in the list view-we are interested only in the DisplayName property here.

procedure TfrmMain.lvMainAddFolder(Sender: TObject;
  AFolder: TShellFolder; var CanAdd: Boolean);

//
// This procedure is triggered by the 
// TShellListView OnAddFolder event for every
// file or folder encountered.
// The AFolder parameter can represent either  
// a folder or a file. Only file
// names are checked.
//
// The CanAdd var parameter is set to False 
// if current file is not to be added
// to the list.
//

var
  mp : TMatchPosition; // to satisfy the call to CheckString

begin
  CanAdd := True;
  if (AFolder.IsFolder) or rbNone.Checked then
    Exit; // don't check if folder, or matching turned off

// now see if this is what we want
  if rexMain.MatchPattern.Count > 0 then
  try
    CanAdd := rexMain.CheckString(AFolder.DisplayName, mp);
  except
    CanAdd := False;  // in case of invalid match pattern
  end;
end;

I set up the OnAddFolder event handler lvMainAddFolder, shown in the code above, using the Object Inspector. It simply checks the display name against the match pattern and sets the CanAdd parameter accordingly. The mp variable is not used, but must be present to satisfy the CheckString calling signature. I set up a try-except block around the call to CheckString to trap malformed match patterns and other errors. You might want to add a message box or some other kind of error handling here.

procedure TfrmMain.ChangeClick(Sender: TObject);
//
// This OnChange event handler procedure 
// is called to update the TShellListView
// whenever a control state is changed.
// It sets some TShellListView properties, sets up the 
// RegEx for the match,
// and then calls its Refresh procedure.
//
var
  mp : string;  // match pattern

begin

  rexMain.MatchPattern.Clear;
  if Length(Trim(edtFilter.Text)) > 0 then
  begin
// load the RegEx filter pattern
    if rbDOS.Checked then
// DOS style filter
    begin
      if (not rexMain.FileMasksToRegEx(edtFilter.Text)) then
      begin
      // invalid DOS match pattern, perhaps 
      // some error msg in order here
        Exit; 
      end;
    end
    else
// RegEx filter
    begin
      if edtFilter.Text[1] <> '^' then
        // make sure pattern is anchored to start of name
        mp := '^' + edtFilter.Text 
      else
        mp := edtFilter.Text;
      rexMain.MatchPattern.Add(mp);
    end;
  end;

// see if the user wants hidden files and/or folders displayed.
  if cbHidden.Checked then
    lvMain.ObjectTypes := lvMain.ObjectTypes + [otHidden]
  else
    lvMain.ObjectTypes := lvMain.ObjectTypes - [otHidden];

  if cbFolders.Checked then
    lvMain.ObjectTypes := lvMain.ObjectTypes + [otFolders]
  else
    lvMain.ObjectTypes := lvMain.ObjectTypes - [otFolders];

// refresh the list. This will trigger 
// TfrmMain.lvMainAddFolder for each
// file and folder encountered.
  lvMain.Refresh;

end;

The ChangeClick event handler shown in Figure 3 is set with Object Inspector as the OnClick event for many of the controls. Each time it fires, the RegEx match pattern is set up from the text box and the appropriate options are set for the list view. Finally, the list view is refreshed, triggering a series of calls to OnAddFolder.

One handy feature of the SysTools regular expression package is the FileMasksToRegEx function which enables the use of traditional Windows file masks as match patterns. Multiple patterns can be entered if separated by semicolons. I use this function when DOS-style masking is selected with the radio buttons. Once again, I wimped out on the error handling for this function-you might want to do more.

I added one wrinkle to the ChangeClick procedure to make sure that the regular expression match pattern is anchored to the start of the file name string by making sure the pattern starts with a caret. Otherwise, the pattern does not work as one might expect. Regular expressions work differently from normal Windows file matching patterns. If you are unfamiliar with regular expressions, I suggest you check out Chapter 15 in the Systools documentation, which is available as a separate download file on SourceForge.

So there you have it. As I promised, the coding turned out to be quite simple once I discovered the right hooks into the ShellListView component. The test harness app still doesn't do anything particularly useful as it stands, but I plan to add more functionality to it in my next project. Stay tuned, and of course, download the source code (including the exe) for this article.

From Zarko Gajic,
Your Guide to Delphi Programming.
FREE Newsletter. Sign Up Now!
Newsletters & RSSEmail to a friendSubmit to Digg
 All Topics | Email Article | | |
Advertising Info | News & Events | Work at About | SiteMap | Reprints | HelpOur Story | Be a Guide
User Agreement | Ethics Policy | Patent Info. | Privacy Policy©2008 About, Inc., A part of The New York Times Company. All rights reserved.