TextEdit

This directory contains the source code for the Objective-C version of TextEdit, which is a simple text editor based on the NSText and NSDocument subsystems of Cocoa.

Scroll about halfway down this document for an overview of the source files in TextEdit.

Changes in TextEdit 1.8 (Zin):

• By adopting the Ubiquity entitlement, TextEdit is now able to use iCloud document storage.  This is automatic for an Auto Save app like TextEdit; one change TextEdit had to do was to switch from overriding NSDocumentController's -runModalOpenPanel:forTypes: to -beginOpenPanel:forTypes:completionHandler:, in order enable NSDocument to present the open panel non-modally when appropriate.
• TextEdit's nib files have now all been converted to use autolayout.
• TextEdit adopts NSScrollView's zooming feature. This is active even in Wrap-to-Window mode.  In Wrap-to-Page mode, the zooming pop-up has been removed, which enables TextEdit to use overall scrollers. There is a new menu to control zooming level, or the user can just zoom in and out using magnify gestures.
• For new rich text documents, TextEdit now uses fractional glyph advancements, rather than integral. The former provides for better looking text with more natural spacing, and is especially nicer looking in higher resolution modes.  TextEdit calls -[NSLayoutManager setUsesScreenFonts:] to change this setting. Documents coming from older OS X systems are still displayed using integral glyph advancements, for document compatibility.

Changes in TextEdit 1.7 (Lion):

• In Mac OS X Lion, TextEdit adopts Auto Save. TextEdit overrides the NSDocument class method +autosavesInPlace and returns YES to enable saveless.  In addition there is a bunch of code to support TextEdit's ability to deal with multiple file types and to transparently transform between them (RTF to RTFD for instance).  Not all applications will need this level of customization.  Note that TextEdit's "File" menu is automatically updated by the AppKit to reflect Auto Save changes, such as the replacement of the "Save As…" item with "Duplicate."  This allows the same menu to be used on 10.6 and 10.7 systems.
• Support for the AutosaveDelay preference has been removed; along with the UI to set it. (That preference will still control how often SnowLeopard version of TextEdit automatically saves, for backup purposes.)  For 10.7 there is a new AutosavingDelay preference, which is set to 30 by default.  It controls how frequently the new save-in-place saving kicks in. It can be changed only from the command line for demo and experimentation purposes, if needed.
• TextEdit enables the new "inspector bar" feature of NSTextView.
• TextEdit now enables rewrapping (rather than scaling) of the contents to the page size when printing "wrap-to-window" documents. The setting is controlled in the print panel, and is remembered between invocations of the panel (and across launches).
• TextEdit enables the attachment preview via QuickLook.
• TextEdit now supports vertical text layout.
• TextEdit showcases the new text finder bar, with -[NSTextView setUsesFindBar:YES]. 
• TextEdit opts into Automatic Termination. However, this opt-in is done automatically by the system. Normally TextEdit would have had to put an entry for NSSupportsAutomaticTermination in its Info.plist or call -[NSProcessInfo setAutomaticTerminationSupportEnabled:YES].
• In TextEdit's "Wrap to Window" mode, document layout and wrapping is determined by window width. Because of this, TextEdit reacts to changes in scroller style by changing the size of the window rather than the size of the content.  This explains the code in DocumentWindowController to observe and react to scrollerStyle, as well as the code to persist and restore this setting as a part of state restoration.
• TextEdit is sandboxed in Lion.

Changes in TextEdit 1.6 (SnowLeopard):

• As with most system apps, TextEdit is now 64-bit by default.
• The dock icon for TextEdit now has a "New Document" item.
• "Link…," "Table…," and "List…" menu items have been relocated in the menus. 
• "Link…" and kerning commands now have command key equivalents.
• There is now an explicit menu item, "Attach Files…," for inserting attachments (images, documents, arbitrary folders) into documents.
• TextEdit's services menu offerings have been tweaked to fit better with the 10.6 services menu architecture: The titles are improved, and "Open Selected File" is declared with an entry for NSRequiredContext, so that it only shows when a file path is selected.
• TextEdit enables concurrent document opening for all types except HTML and WebArchive.  This is achieved by a single-line override of the method -canConcurrentlyReadDocumentsOfType: in TextEdit's subclass of NSDocument.m.
• TextEdit's preferences panel is now bindings-based, greatly simplifying the Preferences class and allowing better support for Sudden Termination.
• TextEdit now has menu items and preferences for controlling spelling and grammar checking and correction, data detectors, as well as text substitution and transformation features.  Text substitution and automatic spelling correction are enabled by default. The list of substitutions can be controlled via the new "Language & Text" pane in System Preferences.
• TextEdit now has "Save As PDF..." menu item. It allows generating PDF for the current document without going through the print panel. Note that this functionality can easily be achieved in an NSDocument-based app on SnowLeopard by simply omitting the value of the NSPrintSavePath key (and the new NSPrintJobSavingURL key). TextEdit does this. On Leopard, some lines of code are needed; here is what TextEdit had to do before it switched simply omitting NSPrintSavePath from the -[NSDocument printDocumentWithSettings:showPrintPanel:delegate:didPrintSelector:contextInfo:] call:

    NSSavePanel *savePanel = [NSSavePanel savePanel];
    // Come up with a default name for the saved PDF
    NSString *defaultPDFFileName = [[[[self fileURL] path] lastPathComponent] stringByDeletingPathExtension];	// May end up nil
    [savePanel setRequiredFileType:@"pdf"];
    [savePanel setCanSelectHiddenExtension:YES];
    if (defaultPDFFileName) [savePanel setNameFieldStringValue:defaultPDFFileName];
    // Use the 10.6-introduced sheet API with block handler
    [savePanel beginSheetModalForWindow:[[[self windowControllers] objectAtIndex:0] window] completionHandler:^(NSInteger result) {
	if (result == NSFileHandlingPanelOKButton) {	// Only if not cancelled
	    [self printDocumentWithSettings:[NSDictionary dictionaryWithObjectsAndKeys:NSPrintSaveJob, NSPrintJobDisposition, [savePanel filename], NSPrintSavePath, nil] showPrintPanel:NO delegate:nil didPrintSelector:NULL contextInfo:NULL];
	    // Properly hide the file name extension
	    if ([savePanel isExtensionHidden]) [[NSFileManager defaultManager] setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSFileExtensionHidden, nil] ofItemAtPath:[savePanel filename] error:NULL];
	}
    }];

Changes in TextEdit 1.5 (Leopard):

• TextEdit is now NSDocument-based and thus provides many features provided or enabled by NSDocument, such as correct file tracking. 
• Due to the NSDocument changes, some open/save options have been removed from TextEdit's preference panel.
• TextEdit now has "autosave" functionality.  By default documents are autosaved every 30 seconds, but this can be changed via the preferences panel.
• TextEdit now uses 64-bit compatible types such as NSInteger and CGFloat. It can be built and used in 64-bit mode.
• TextEdit supports two new document formats, Word 2007 (.docx) and OpenDocument (.odt).
• TextEdit enables non-contiguous text layout, which enables much faster processing of large files.
• TextEdit now stores encoding of plain text files with the file, as an extended attribute. (Please refer to the Foundation release notes for technical details on this.)  The stored encoding enables the files to be interpreted correctly upon reading. With this change, the default encoding for saving in TextEdit has also been changed to UTF-8. This can of course be overridden through the preference panel, or on a per-file basis in the save panel.
• There is now a “Select Line” panel, which lets you go to a line or range of lines.  It’s implemented as subclass of NSWindowController.
• TextEdit has a subclass of NSViewController to manage a print panel accessory that enables changing printing of headers and footers.  The "Number pages when printing" option has been removed from the preference panel.
• Another NSWindowController subclass, DocumentPropertiesPanelController, manages the properties panel. This class shows how to implement an inspector-style panel using bindings, and provide reasonable edit-commiting behaviors (for instance, when the panel is dismissed, when document windows are saved or deactivated, etc). With the introduction of this class, some messy code in the Document class to manage undos in the presence of "continuously updating" text fields have been removed.
• As always, TextEdit highlights some of the other new features in the Cocoa text system, such as ability to do smart quotes or links.

Changes in TextEdit 1.4 (Tiger):

• TextEdit now uses new NSError and NSAlert facilities to put up better error messages on failed file system operations.
• TextEdit now has a preference to enable the use of the new Cocoa feature to auto-number printed pages. Although the feature enables sophisticated customization of the headers and footers by the application, TextEdit just uses the default format.
• The new "Document Properties" panel enables setting properties such as author, company, comment, etc.  The fields in the panel are hooked up to the document via bindings, and are imported by the Spotlight text importer for easy searching. The preferences panel has fields where you can specify default values for properties (for which default values are appropriate).
• TextEdit can now save WordML, HTML, and Safari WebArchive documents.  In the preferences panel you can specify a number of options for controlling the type of HTML that is generated.
• TextEdit has a few menu items to support new text system features such as tables, lists, and link editing panel. Other than the new menu items, these features are pretty much free for TextEdit.  Be sure to read about new text features in the AppKit release notes.
• The new Insert submenu under the Edit menu enables inserting line break, paragraph break, and page break. Note that the default line break you get by hitting the Return key is actually paragraph break; line break is used to break lines without starting a new paragraph.
• Undo can now be used to undo changes across a save.  Note that on save, TextEdit now calls the new NSTextView method, breakUndoCoalescing, in order to let the text system know that it shouldn't coalesce across a save.
• TextEdit can now process Finder or scripting print requests much better.
• When multiple documents are opened simultaneously, TextEdit now makes the first one key, and opens others behind this first document.  For large numbers of files, this change reduces the overall time before the application is responsive.  In addition, in these cases TextEdit now does foreground (that is, uninterruptible) text layout only for the front-most document. This also helps greatly improve the time to make the app responsive when the files are large.
• TextEdit now uses -[NSParagraphStyle defaultTabInterval] to manage tab stops in plain text documents, rather than having to fix up tab stops after every change. This change removes a good number of lines, including the routine fixUpTabsInRange: and the need to observe textStorageDidProcessEditing:.
• If user's default encoding choice and their defaultCStringEncoding don't work for a plain document, then try Unicode, assuming it's in their encodings list.
• When an RTF document becomes RTFD, the user now has the option to save the document with the same name (this happens by saving a .rtfd file and thrashing the .rtf).
• NSSavePanel's setAllowsOtherFileTypes: method is now used in the plain text case, to use a ".txt" extension unless the user specifies otherwise.
• Checkbox in the plain text save panel now allows the user the control the ".txt" extension.
• The preferences panel has been redesigned. And although it is a good candidate for using NSUserDefaultsController and bindings, it doesn't yet. It should.
• The Cocoa text system now lets you create multiple, non-contiguous selections when editing text. Option-drag will let you select text in a rectangular area; command-click will let you add new selections to an existing selection. You can then copy the contents of these multiple selected regions as if they were one.  In addition, you can select all occurrences of a word in the Find panel by holding down control when clicking "Replace All" (this exposes the "Select All" command). Finally, you can use the Styles panel to find all text which is of a given style.
• TextEdit's AppleScript command handling for "save" has been improved and made safer.
• TextEdit is still not NSDocument-based. It should be.

Changes in TextEdit 1.3, Mac OS X 10.3 (Panther):

• TextEdit now uses NSTextView's new built-in find panel.  The TextFinder class was removed and the find menu items were changed to send the standard performFindPanelAction: method down the responder chain.
• Command key for "Find Previous" has been changed to cmd-shift-G.
• TextEdit can now open and save Word ".doc" format documents. This support is based on Cocoa's ".doc" support; TextEdit simply adds a format popup in the save panel to allow choosing between RTF and Word formats. Note that because Word files may be loaded lossily, TextEdit will warn when overwriting a Word file.
• TextEdit's "Edit" menu has new menu items: "Paste With Current Style" and "Complete."  These are not yet in the standard IB templates, but might get added in the future.
• Default ruler tab stops setting now depends on the user's measurement units.
• When saving a document, TextEdit now checks to see if the file on disk has the same modification date as the document when it was last opened or saved; if not, TextEdit puts up an alert. The same kind of check is made when an already open and unsaved document is opened again from Finder.
• TextEdit's link handling code was made more robust by dealing with NSURL links and not assuming the link attribute is of type NSString. TextEdit can now also read/write and copy/paste links (thanks to changes in Cocoa's RTF handling). However note that TextEdit does not provide UI to let users create links.
• Eliminated special code to deal with setting of open/save panel directories; we now go with the default behavior of open/save panels. The only custom code left is to deal with the "OpenPanelFollowsMainWindow" preference, which is by default off.
• TextEdit calls setAllowsDocumentBackgroundColorChange:YES to enable user setting of the text background color. It also saves this color as a document attribute.
• And as usual, TextEdit automatically benefits from many changes made in Cocoa. These include enhancements in text editing, new ruler, additional character style controls in the font panel, improvements in RTF handling, new open/save panels, etc. 

Changes in TextEdit 1.2, Mac OS X 10.2 (Jaguar):

• Default rulers for new documents now have 12 tab stops 1/2 inch apart (instead of the giant 1 inch tabs).  Rulers are also shown by default for rich text documents. This can be disabled in the preferences panel.
• TextEdit's Info.plist declares CFBundleDisplayName to allow localization of its name in Finder and open/save panels. It also declares MIME types for text and RTF documents.
• TextEdit can now overwrite locked files (like it does files with no write-permissions), assuming the user confirms.
• File names received via the "Open File" service will now be sanitized further (by deleting all lines but the first and trimming spaces), if the original name doesn't work.
• The list of encodings which appear in the various encodings popups (open/save panels, Preferences panel) can be customized. A new class, EncodingManager, implements this behavior and also collects most of the other encoding-related implementation in TextEdit.

Changes TextEdit 1.1, Mac OS X 10.1:

• Added support for saving files with hidden extensions. If TextEdit was an NSDocument based app, this would be automatic; but it's not, so it requires a few lines of code to (1) enable the save panel to show the checkbox to allow user to set state of hidden extension, and (2) pay attention to the flag and save it with  the document. In addition, TextEdit uses the displayNameAtPath: method in NSFileManager to display document names properly (in the few places it does).
• Thanks to changes in Cocoa, TextEdit's recent documents menu will now correctly track documents whose volumes or enclosing folders have been renamed. Note that TextEdit itself does not yet track open documents whose volumes or folders have been renamed. This is automatic in NSDocument, but TextEdit isn't NSDocument based.
• Added support for filter services; that is, ability to invoke converters which convert arbitrary text documents to a format TextEdit understands. The ability to use filter services is in the Cocoa text system, TextEdit just looks for the "Converted" flag in the document attributes dictionary, and if the document was converted, assures it is assigned a new name when saving.
• In order to preserve any aliases to saved documents and other relevant information (such as icon locations, creation date, etc) in saved documents, TextEdit now attempts to exchange the contents of resaved documents into the previous version of the files. This brings additional user benefits, but also introduces some complexity into TextEdit's save routine (in DocumentReadWrite.m) as exchangedata() is not supported on all file systems and does not work with folders (in TextEdit's case, RTFDs). In those cases TextEdit falls back to moving files around as it used to. In any case, all this is automatic for NSDocument-based applications, and some day we hope to make this functionality easier to access from non-NSDocument based apps as well.
• Added support for read-only documents. RTF documents can be saved as read-only, and will be displayed without a blinking cursor when opened. SimpleText "ttro" documents will also be opened the same way. Users can make a document editable through a menu selection.
• The untitled document which is brought up automatically when TextEdit is launched or brought forward will be closed if it is left untouched and the user opens another document. TextEdit currently does this itself (see the "transientDocument" code in Document.m), but it would be good to pull this into the AppKit at some point.
• A few more encodings have been added to the default open panel encoding popup.
• Changed app signature of TextEdit to 'ttxt', which matches that of SimpleText, so that old documents will open in TextEdit and not launch SimpleText in Classic. However, because screen grabs from Mac OS 9 have SimpleText's creator, also added code to recognize these files and pass them off to the user's default image viewer. 
• Fixed a bug in TextFinder.m where "replace all" could fail and raise if the search string occurred multiple overlapping times in a selection; for instance, "aa" being replaced in the word "craaash".
• Preferences object would always write the preferences out on quit, without checking to see if they actually changed. It now does a check.

Changes in TextEdit 1.0, Mac OS 10.0:

• Now using the third possible return value (NSTerminateLater) into applicationShouldTerminate: delegation message to indicate to NSApplication that user is still deciding.
• Got rid of showInfoPanel:; all it did was to allow adding "c" (for Objective-C, as opposed to Java) to the version number. Now we just use the standard about panel support and don't add the letter.
• We now update the find pasteboard when a new find string is set, rather than on app deactivation, to eliminate any race issues with apps picking up the pasteboard setting during app swapping.
• Switched to sheet APIs for NSPrintPanel and NSPageLayout.
• Small changes for adapting to scripting framework reorg.
• Stopped implementing textView:draggedCell:inRect:event: delegate method; instead now using textView:writablePasteboardTypesForCell:atIndex: and textView:writeCell:atIndex:toPasteboard:type:. This allows more uniform treatment of attachments for dragging.
• Added an "app signature". This in general is not needed for applications on Mac OS X. However, Internet Config still requires it, and because TextEdit is a system app which needs to bind to default text documents, we have one. Note that this "app signature" is used only for this purpose, and is not used as a "creator code" on documents saved by TextEdit.
• Toggle rich/text menu item is now cmd-shift-T (same as Mail).
• In order to be more compatible with the other big RTF writer app out there, TextEdit now uses the "PageSize" document attribute correctly --- that is, for the page size rather than view size. The view size is saved using the new "ViewSize" attribute. In addition, TextEdit now also saves the viewing mode (Wrap to Window or Wrap to Page), and zoom.
• TextEdit now appends "txt" to plain text documents, a feature which can be turned off in the Preferences panel. Also, if the user enters an extension, TextEdit uses a new NSSavePanel delegation method to catch this and give the user a chance to decide whether to append the "txt" extension or not.
• Note that being the delegate of the save panel (which occurs only in plain text mode) has created an issue: When the save panel is up, the document still ends up being in the responder chain, causing menu items such as "Save" to get validated and thus enabled. We fix this by setting a "runningSheet" instance variable, and looking at that in Document's validateMenuItem:. However, some menu items are still inappropriately enabled, such as "Make Rich Text." We will be looking into a general fix for this issue.

Changes in Mac OS X Public Beta: 

• TextEdit now links against Cocoa.framework, instead of AppKit and AppKitScripting separately.
• Default place to save documents is the user's Documents folder.
• TextEdit no longer sets a miniwindow icon. With the miniaturization in the dock, most applications can get away without miniwindow images.
• The Info.plist now includes a more complete set of types and extensions, allowing TextEdit to see more document types (including untyped files).
• TextEdit uses the CFAppleHelpAnchor key in its Info.plist to declare the section of the Apple help book to be consulted when "help" is used. This is an Apple-internal key, and should not be used by third-party applications.

Changes in Mac OS X Developer Preview 4:

• TextEdit is now a new Project Builder project.
• By using an additional autorelease pool, "Replace All" was speeded up greatly for the case of many small replacements in a large file.
• TextEdit will now no longer try to automatically attempt to overwrite read-only files when saving, asking for confirmation instead. You can disable this behavior through the preferences panel.
• Whenever a save fails, TextEdit would automatically bring up a "save as" panel to get an alternate place to save the file.  It no longer does this.
• TextEdit can now open SimpleText files.
• On save, TextEdit will now put up a warning panel if it can't save a file in the original format (for instance, RTF files which have been promoted to RTFD, or formats which are read-only, such as HTML or SimpleText).
• The printInfo in Document.m is now created lazily, when first accessed.
• TextEdit uses the new document modal (sheet) APIs to bring up document modal panels. These APIs allow each window to have a "doc modal" panel of its own up, but achieve non-modality of the app by working through callbacks. Because TextEdit is not NSDocument-based, it implements a lot of the logic for putting up the panels itself. The intent is to move a lot of this code into the AppKit at some point in the near future.

Major source files and what's interesting about them:

Document.m

Subclass of NSDocument.  One instance of this is created for every document (new or saved) in TextEdit.  

The text contents (characters, attachments, attributes) of the document are kept in an instance of NSTextStorage. 

Document overrides readFromURL:ofType:error: since it wants to specify the user options for opening files, such as encoding and whether to open rich text documents as plain.  These come from DocumentController, who hangs on to the values chosen by the user when the open panel was displayed.  The main reading workhorse is readFromURL:ofType:encoding:ignoreRTF:ignoreHTML:error:, which does a bunch of text-specific stuff to load the document with specified options.

Document also overrides writeToURL:ofType:error: since it wants more control over the writing process.  However it will sometimes invoke the NSDocument version of this method by calling super, and to support that there is an override of fileWrapperOfType:error: as well.

Note that even with these customizations, TextEdit is able to benefit from many of NSDocument's built-in saving features, such as its ability to track documents on disk after renaming, safe-saving, hidden extension handling, autosaving for recovery purposes, etc...

Document hangs on to a number of properties, such as the scaleFactor, viewSize, readOnly, backgroundColor, hyphenationFactor, etc. These are kept up to date with user's choices by various methods, and written out to rich text documents when saving.

There are also document properties that are settable by the user, such as author, company, keywords, ... These are stored in instance variables in Document and updated via bindings from the document properties panel.  Like other properties, these are also written out with rich text documents.

In order to enable these properties to be undoable, Document implements setValue:forDocumentProperty:.  This is called from the standard KVC method setValue:forKey: for properties we want to be undoable. By registering setValue:forDocumentProperty: as the callback in NSUndoManager, we workaround the NSUndoManager bug where prepareWithInvocationTarget: fails to freeze-dry invocations with "known" methods such as setValue:forKey:.

Note the "trick"  of providing string localizations in comments as a way to get genstrings to pick up the localizations when there are actually no 
explicit corresponding calls to NSLocalizedString and variants in the code. This allows us to provide menu titles for changes to document properties.  (Search for "For genstrings".)

Override of printOperationWithSettings:error: enables TextEdit to customize its printing by first making sure the text for the whole document is laid out before printing, and by adding an print accessory view to control whether pages should be numbered or not.

In the printInfo for the document, TextEdit sets horizontal pagination to NSFitPagination. This allows the text to be printed with the same wrapping as on the screen. In "wrap-to-window" mode this means the text might need to be scaled smaller when printed.  This behavior should really be controlled by a check box in the TextEdit accessory on print panel, but this hasn't happened yet.

As you will note, there is a good deal of code to deal with encoding of the characters in the document when the document contains plain text. The instance variable documentEncoding stores the encoding of the document; this is either deduced from the file or specified by the user when the document is opened. Keeping this encoding around allows the document to be saved with the same encoding as it was read. (When in memory the character encoding of the document is somewhat meaningless, because the characters in the document are stored in an NSString, whose backing stores are always expressed in terms of Unicode characters. The encoding determines how to save the document when saved as plain text.)

DocumentWindowController.m

Subclass of NSWindowController for managing the Document class's interaction with its document window and views.

DocumentWindowController is responsible for creating the NSLayoutManager and one or more NSTextViews (depending on whether "wrap to page" mode is selected). These provide the UI layer for the text backing, which is stored in the Document class.

DocumentWindowController establishes a number of connections:

It observes the printInfo, richText, viewSize, and hasMultiplePages properties of the document.  When it gets told these are changed (in observeValueForKeyPath:ofObject:change:context:, it updates the window or view as appropriate.

It observes the backgroundColor of the text view and scaleFactor of the scrollView, communicating any changes in these to the document. (These properties are changed directly in the view by the user.)

DocumentWindowController binds its layout manager's hyphenationFactor to document.hyphenationFactor,  and text view's editable to document.readOnly (through a negating value transformer). These allow reflecting changes from these in UI without any intermediate glue code.

setHasMultiplePages: determines whether the document is in wrap-to-page mode or not; study this method, addPage, and removePage to see how to create and manipulate NSTextViews programmatically.

The method textEditDoForegroundLayoutToCharacterIndex: shows how to get the text system to lay text out in the foreground up to a certain character location. By default the text system does its layout in the background, which allows bringing up the window fairly quickly. The user can even edit, print, or save the document while the background layout is going on. This method enables having the first portion of the document already laid out. Note that this was useful from a user point of view in Tiger, where the scrollbar for the document raced down the page as background layout happened, but it's less interesting in Leopard when non-contiguous background layout is enabled. However, we still do it, and this method is also useful for when printing the document (since TextEdit doesn't rewrap or relayout when printing and just uses its existing layout into).

This class implements a number of NSTextView, NSLayoutManager, and NSWindow delegate methods.  

textView:clickedOnLink:atIndex: is overridden to allow opening links that represent text files directly in TextEdit, revealing other files in Finder, and opening non-local file URLs in the user's browser.  textView:doubleClickedOnCell:inRect:, textView:writablePasteboardTypesForCell:atIndex:, and textView:writeCell:atIndex: allow opening and copying attached documents.  

layoutManager:didCompleteLayoutForTextContainer:atEnd: controls adding/removing of pages as the document is edited or laid out.

windowWillUseStandardFrame:defaultFrame: method provides "standard" and "user" sizes for resizing (or right-sizing) the document window via the green button.

This class takes care of setting the document as non-transient after moving or resizing the window.

DocumentController.m

Subclass of NSDocumentController. Most NSDocument-based applications don't need to subclass NSDocumentController. TextEdit does this to provide "transient document" behavior, as well as customizing the open panel.

When the document controller is initialized, it binds its autosaving delay field to user defaults, in order to be automatically notified when the user changes this preference.

Transient document is the untitled document put up when TextEdit is first launched, or activated with no other documents open.  If the user makes no changes to this untitled document before opening a new document, we get rid of the untitled document and visually replace it with the opened one. NSDocument doesn't yet have support for this, hence the code in TextEdit.

The open panel customizations include an accessory view which lets the user choose whether to load rich text documents as plain, and the text encoding to be used for plain documents.  These settings are then made available (in the context of the open operation) to the Document class via the methods lastSelectedEncoding, lastSelectedIgnoreHTML, and lastSelectedIgnoreRich.

MultiplePageView.m

In wrap-to-page mode there is one NSTextView per page. MultiplePageView is the top level view which groups all of these views.  It is inserted as the document view of the scroll view in the document window. MultiplePageView is fairly simple, providing support for conversions between page numbers and rects, and drawing the background for the pages.

A possible enhancement to this class would be to have it allow the user to manipulate the page margins by dragging guides around. An advanced exercise would be to add custom markers to the ruler to allow changing the page margins via the ruler as well.

ScalingScrollView.m

Contains ScalingScrollView, a subclass of NSView to implement a scroll view with a popup to allow setting the zoom factor. This class is fairly generic and can easily be used in a variety of cases.  The scaleFactor property can be observed.

In TextEdit, this class is always in use in every document window. However, in wrap-to-window mode, the horizontal scroller and the scale popup are disabled, and the scale is set to 100%.

Preferences.m

An NSWindowController subclass that controls the preferences window. Since the switch to a bindings-based preferences window, this class has become greatly simplified. However, some preferences, such as HTML saving options and font settings, require special action and are still handled here.

The preferences controller also makes sure that the window size settings are valid; if the user enters an invalid dimension, the field is reset to its previous value.

Controller.m

This file contains the central controller object for TextEdit.  With TextEdit's move to NSDocument, and creation of several other controllers, this class has shed some weight in Leopard and has fewer responsibilities.

In its +initialize method this class registers default values for all preferences (besides fonts, which are handled directly by NSFont's facilities for "user fonts").

Controller object also provides the little support necessary for allowing TextEdit to provide the "Open File" and "Open Selection" services to other applications. All that needs to happen to support this powerful feature is an entry in the Info.plist file listing the services provided, and methods in Controller to respond to the services requests: openFile:userData:error: and openSelection:userData:error:.

Since openFile:userData:error: may be given arbitrary text as file names, it does some clean-up and extra checks. For instance, it trims whitespace and does "~" expansion (where "~" is used as a shortcut to mean "home folder"). 

EncodingManager.m

This file provides the class EncodingManager, which does most of the sophisticated text encoding related stuff.  This class also manages a panel which lets the user customize the list of encodings available in the application.

In addition, EncodingManager provides the ability to load the accessory view used in open and save panels.

The EncodingPopUpButtonCell class implements a popup which lets the user choose from a list of encodings. The list can be customized via an entry in the popup which brings up a customization panel.  The class's instances assure that they are updated (via notifications) after any change in the customization panel.

LinePanelController.m

Manages the "Select Line" panel. Subclass of NSWindowController. Uses NSScanner to parse user input.  

The beefiest code in this class is getRange:inTextView:fromLineSpec:toLineSpec:relative:, which figures out the range of characters to be selected based on user's input. Note that this input can be an absolute line (1..number of lines), an absolute range (two line numbers separated by a dash), or a relative line or range (number prefixed by plus or minus).

DocumentPropertiesPanelController.m

Manages the “Document Properties” panel. Yet another subclass of NSWindowController.

This class is an example of implementing an inspector-style panel with bindings.  It goes out of its way to take care of one area not covered automatically by the kit, namely committing of editing (that is, the user has typed a value in a field but not hit return or tab to leave the field) when the user deactivates the document window or closes the properties panel.  Since there is no validation necessary for any of the fields in this panel, always committing is a straightforward solution.

DocumentPropertiesPanelController keeps track of the current active document by observing mainWindow.windowController.document on NSApp, and updating the KVO-compliant property inspectedDocument when these are received.   

All the fields in this panel are hooked up to the inspectedDocument through an NSObjectController, using the corresponding properties in Document—author, company, keywords, etc. Any new fields can be added to the panel with no changes to this class.

DocumentPropertiesPanelController also implements the NSEditorRegistration protocol, objectDidBeginEditing: and objectDidEndEditing:. As the user starts/stops editing in the fields in the panel, these methods are invoked. DocumentPropertiesPanelController passes these straight through to the inspectedDocument. In turn NSDocument commits/discards editing as necessary when saving or closing documents.  In addition, whenever a document is deactivated, any editing in the currently edited field is committed; this is done in stopInspectingDocumentOfWindow:.

In order to commit editing when the properties panel itself is closed or deactivated, DocumentPropertiesPanelController listens to the NSWindowDidResignKeyNotification notification on the panel itself. 

Although the single instance of this class is created at launch time, none of the notifications are signed up for until the panel is brought up the first time, in windowDidLoad.  So this panel has minimal performance impact when not used.

Finally, this class implements a method to toggle the panel (if active, order it out; otherwise, activate, ordering in if necessary), and a validateMenuItem: method to update the title of the "Show Properties" menu item.

This class is mostly reusable; just the name of the class, the nib, validateMenuItem:, and reference to [Document class] need changing. It can be made into an semi-abstract NSWindowController for inspectors by removing these pieces into a subclass.

PrintPanelAccessoryController.m

A subclass of  the new NSViewController class. Used for adding an accessory view to the print panel. In the case of TextEdit, the accessory view contains just a checkbox, controlling whether or not to include headers and footers on printouts.

PrintPanelAccessoryController has a BOOL pageNumbering property to determine whether headers and footers should be included. The property is actually not stored in this class, but set directly in the printInfo, which is the representedObject.

The method keyPathsForValuesAffectingPreview is overridden to return "pageNumbering" as a way to declare that any changes to this property should update the print preview displayed in the print panel.

An override of localizedSummaryItems provides the info to be displayed in the print panel's "Summary" pane.

These two methods above are part of the NSPrintPanelAccessorizing protocol.