Matt Gemmell

TOLL is available now!

An action-thriller novel — book 2 in the KESTREL series.

★★★★★ — Amazon

A Better TextFinder

source 2 min read

The TextFinder class (which supplies TextEdit's find and replace functionality) is a bit quirky. 
I've tweaked it to make it better (and more sensible).
The following aspects of TextEdit's find/replace struck me as a little peculiar:
  • All of the options in the Find submenu of the Edit menu are always available, even if there's no document open. Surely they should only be available if there's something to search in?
  • "Replace All" works as you'd expect. "Replace" and "Replace and Find", however, work by replacing the currently-selected text, regardless of what that text is - they do not replace the next occurrence of the Find string (try it for yourself in TextEdit). This means that they will always insert the replace string, even in an empty document! I find that a bit strange.
  • TextEdit doesn't remember the last Replace string between launches.
So, I changed it. My version of the TextFinder class now does these things:
  • Remembers the last Replace string between launches, on a per-application basis.
  • Now has a "Replace and Find Previous" action method, to go with "Replace and Find" (Next).
  • "Replace", "Replace and Find" and "Replace and Find Previous" now replace the next occurrence of the Find string, first checking to see whether the selection is such an occurrence. They also respect the state of the "Ignore Case" checkbox in the (included) Find Panel.
  • We now sensibly validate menuitems which target us, as follows:
    • Generally, the items in the Find submenu are only available if the first responder is an NSTextView.
    • "Use Selection for Find" and "Scroll to Selection" are only available if there's a selection.
    • "Find Next" and "Find Previous" are only available if the Find string isn't an empty string.
    • "Replace", "Replace and Find", "Replace and Find Previous" and "Replace All" are only available if the Find string isn't an empty string, and the relevant NSTextView is editable.
    • By default, the "Find.." (show Find panel) item is always enabled. I did this since it's obviously possible to close the last document window of an app and still have the Find panel open, and I don't like to let the user close a reusable panel which they can't immediately reopen. You can easily tweak that behaviour to your needs (by editing the -validateMenuItem: method).
So, feel free to <a href="" target="_top">download the improved version</a>. 
It includes the two TextFinder class files, and a nib with the Find panel. Note that 
the nib is identical to that included with TextEdit, except for changes to the tooltips for the "Replace" and "Replace and Find" buttons 
(to reflect the new functionality), so feel free to use your existing nibs if they call the correct action methods.

You can either just replace TextEdit's "TextFinder.h" and 
"TextFinder.m" files and recompile, or you can use this class in your 
own apps. To use it in your own apps, add the files to your project, instantiate a TextFinder in 
your MainMenu.nib, and hook up the appropriate menuitems. The rest 
should work automagically whenever an NSTextView has the focus. TextFinder works with styled text, reads from and writes to the 
global Find pasteboard, and is capable of replacing either within the selection or the entire NSTextView, and can be set to ignore 
case if required. If the Find panel is open, the number of items replaced is reported.

Credit for the TextFinder class goes to Ali Ozer at Apple, and many thanks goes to whomever decided to include TextEdit's source in 
the dev tools examples! As always, enjoy.