When developing applications in Delphi, it is common to have a TImageList that holds the various glyphs used by the application. Those glyphs can then be used by other components like TToolButton by assigning the number of a glyph to the ImageIndex property. When adding new glyphs to the application, the easy solution is to add them to the end of the image list, so existing glyph number remain unchanged. But over time this can make a mess of the image list, making it harder to find existing images in the list that need to be reused by newly added components.
If you want to keep the image list organized, you’ll want to insert new glyphs in the middle of the list, next to the glyphs they relate to. To make sure the existing glyphs are still referenced correctly, need a way to increment the ImageIndex properties with numbers equal or greater to the number of the glyph being inserted. Fortunately, you can easily generate a regular expression that matches these numbers with RegexMagic. Actually incrementing the numbers requires arithmetic. Regular expressions don’t do math. But PowerGREP can do simple math on regex and group matches. So can a single line of Delphi code.
Below you’ll find two examples, one using PowerGREP and another using some quick Delphi code, to increment ImageIndex properties like “ImageIndex = 123” in DFM files. The example also handles constants declarations like “imgSomeName = 123” and property assignments like “ImageIndex := 123”. You can easily adjust these examples to handle differently named identifiers. You can these examples as “Real world: increment ImageIndex constants” in the RegexMagic library.
First we prepare PowerGREP for a new search-and-replace.
Now we can generate the regular expression with RegexMagic.
ImageIndex := 123 ImageIndex = 99 imgSomeName = 256
ImageIndex := 123 ImageIndex = 99 imgSomeName = 256
Now we can prepare the replacement with RegexMagic.
Now we’re ready to perform the search-and-replace with PowerGREP. Resist the urge to copy and paste the regex from RegexMagic into PowerGREP. Instead click the Send button on the Regex panel in RegexMagic. This button only appears if you launched RegexMagic from PowerGREP (or another application that integrates with RegexMagic).
You can now preview or execute the search-and-replace in PowerGREP. You’ll see this regular expression:
\m(?<before10>(?:ImageIndex|img[0-9A-Za-z]{1,64})[\t ]*:?=[\t ]*)(?<number>[1-9][0-9]{2}|[5-9][0-9]|4[2-9])\M
Required options: Case sensitive; Exact spacing.
Unused options: Dot doesn’t match line breaks.
You’ll also see this replacement string:
${before10}%groupnumber:+1%
RegexMagic added an extra capturing group to the regex and a reference to it to the replacement string. This preserves the matches of fields 1 to 9.
What you don’t see in PowerGREP is that RegexMagic also sent the RegexMagic Formula (all your settings on the Samples, Match, and Action panels) to PowerGREP. PowerGREP does store this. Use the Action|Save or Action|Add to Library to save your PowerGREP action. The RegexMagic Formula will be stored inside the action. When you load the action into PowerGREP at a later time and click the RegexMagic button in PowerGREP then PowerGREP will send the RegexMagic formula back to RegexMagic. This way you can edit the formula in RegexMagic and send a newly generated regex (and updated settings) back to PowerGREP.
In this example that will be very handy. Each time you do this search-and-replace, you’ll want to increment a different range of numbers. By saving the RegexMagic formula, PowerGREP lets you do this quickly by launching RegexMagic from PowerGREP again, changing the range of numbers for field , and sending the result back to PowerGREP.
This only works if you always launch RegexMagic from PowerGREP and always use the Send button to commit your regex. If you copy and paste, PowerGREP won’t have the RegexMagic formula, and you won’t be able to use RegexMagic to edit the regex later. You can’t copy and paste a regex from PowerGREP or any other application into RegexMagic.
First, prepare the regular expression using RegexMagic as described in steps 7 through 41 above. Then we can prepare the replacement. Delphi does not have a feature similar to PowerGREP’s match placeholders. So instead we’ll tell RegexMagic to remove the matched number from the replacement. We’ll add the incremented number with some Delphi code.
[[:<:]](?<before10>(?:ImageIndex|img[\dA-Za-z]{1,64})[\t ]*:?=[\t ]*)(?<number>[1-9][0-9]{2}|[5-9][0-9]|4[2-9])[[:>:]]
Required options: Case sensitive; Exact spacing.
Unused options: Dot doesn’t match line breaks; ^$ don’t match at line breaks; Numbered capture; Greedy quantifiers; Allow zero-length matches.
${before10}
var Regex: TPerlRegEx; ResultString: string; Regex := TPerlRegEx.Create; try Regex.RegEx := '[[:<:]](?<before10>(?:ImageIndex|img[\dA-Za-z]{1,64})[\t ]*:?=[\t ]*)(?<number>[1-9][0-9]{2}|[5-9][0-9]|4[2-9])[[:>:]]'; Regex.Options := []; Regex.State := []; Regex.Subject := SubjectString; Regex.Replacement := '${before10}'; Regex.OnReplace := ComputeReplacement; Regex.ReplaceAll; ResultString := Regex.Subject; finally Regex.Free; end; procedure TMyClass.ComputeReplacement(Sender: TObject; var ReplaceWith: string); begin // You can vary the replacement text for each match on-the-fly // ReplaceWith defaults to Regex.Replacement, with backreferences substituted end;
The reason we chose TPerlRegEx over TRegEx is that TPerlRegEx.ReplaceAll uses both the Replacement property and the OnReplace event. When our event handler ComputeReplacement is called, the ReplaceWith parameter holds the replacement string with the backreference already substituted. Since we told RegexMagic to only delete the match of field from the replacement, all we need to do is to write one line of code to append the incremented image index:
procedure TMyClass.ComputeReplacement(Sender: TObject; var ReplaceWith: string); begin ReplaceWith := ReplaceWith + IntToStr(StrToInt(Regex.Groups[Regex.NamedGroup('number')]) + 1) end;