ftp.nice.ch/pub/next/text/apps/eText5.0.93.s.tar.gz#/eText5/eText.subproj/eText.Undo.m

This is eText.Undo.m in view mode; [Download] [Up]

{\rtf0\ansi{\fonttbl\f0\fmodern Courier;\f1\ftech Symbol;\f2\fmodern Ohlfs;}
\margl40
\margr40
{\colortbl;\red0\green0\blue0;\red255\green0\blue0;}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\f0\b0\i0\ulnone\fs24\fc0\cf0 //ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//
\i 	
\b FILENAME
\b0 :	
\b\i0 eText.Undo.m\

\b0 //
\i 	
\b SUMMARY
\b0 :	
\b\i0 Implementation of the Undo subsystem of eText
\b0 \
//	
\b\i CATEGORY
\b0 :
\i0 	
\b Undo
\b0 \
//	
\b\i PROTOCOLS
\b0 :
\i0 	
\b Uses UndoManager
\b0 \
//	
\b\i INTERFACE
\b0 :
\i0 	
\b None
\b0 \
//	
\b\i AUTHOR
\b0 :		
\b\i0 Rohit Khare, portions by Jeff Martin of Bozell.
\b0 \
//	
\b\i COPYRIGHT
\b0 :	
\f1\i0 Ó
\f0\b 1993,94 California Institure of Technology, eText Project\

\b0 //ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b\i Implementation Comments
\b0\i0 \
//		These methods deal with undoManager. We may have some real problems\
//	intercepting the data flow between "user" methods and internal ones\
//	(e.g. paste vs replaceSelWithRTF:)\
//		Unlike Jeff's UndoText, we do ours with undo record grouping and\
//	and without writing new replace... 'Action' methods. At least, we'll try...\
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b\i History
\b0\i0 \
//	10/27/94:	
\b Changed handling of typing undos to attempt coalescing.
\b0 \
//	10/17/94:	
\b Cleaned up for eText5.
\b0 \
//	08/05/94:	
\b Completely Rearchitected for 5.0. RK
\b0 \
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b Imported Interfaces
\b0 \
//\
	#import "
\b eText.Undo.h
\b0 "\
\

\b static
\b0  
\b int
\b0  		
\b undoLastPos
\b0  = -1;\

\b static
\b0  
\b int
\b0  		
\b undoFirstPos
\b0  = -1;\

\b static
\b0  
\b Stream
\b0  	*
\b undoStream
\b0  = NULL;	// 
\i RK: Assumes only 1 Text is being edited
\i0 \

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\b\fc0\cf0 static
\b0  
\b BOOL
\b0  	
\b isTyping
\b0 =
\b NO
\b0 ;\
#define 
\b DELETE_KEY
\b0  (0x08)\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0 \
NXTextFilterFunc
\b0  
\b oldTextFilter
\b0 ; 		// 
\i keep the old text filter around
\i0 \
\

\i @implementation eText(Undo)\

\i0 //ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b eText Undo API\

\b0 //\
- 
\b undoSelChange
\b0 :(const char *)actionName \{ // 
\i Call 
\b before
\b0  transaction\
	if(sp0.cp < 0) return 
\b nil
\b0 ;
\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\i0\fc0\cf0 \
	[self 
\b registerLastTypingUndo
\b0 ];	    // 
\i Clear the queue...
\i0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \
	if(sp0.cp != spN.cp) \{\
		
\b undoStream
\b0  = [[[Stream alloc] init] 
\b openMemory
\b0 ];\
		[self 
\b writeETF
\b0 :[
\b undoStream
\b0  stream] 
\b from
\b0 :
\b sp0
\b0 .cp 
\b to
\b0 :
\b spN
\b0 .cp];\
	\} else \{\
		if(undoStream) [undoStream free];\
		
\b undoStream
\b0  = 
\b NULL
\b0 ;\
	\}\
\
	[undoManager 
\b setActionName
\b0 :
\b NXUniqueString
\b0 (actionName ? actionName : "")];\
 	[undoManager 
\b beginUndoRecordGrouping
\b0 ];\
	return self;\
\}\
- 
\b undoParChange
\b0 :(const char *)actionName \{ // 
\i Call 
\b before
\b0  transaction\

\i0 	int 
\b boP
\b0 , 
\b eoP
\b0 ;\
	\

\i 	if(sp0.cp < 0) return 
\b nil
\b0 ;	\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\i0\fc0\cf0 	[self 
\b registerLastTypingUndo
\b0 ];	    // 
\i Clear the queue...
\i0 \

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \
	
\b undoStream
\b0  = [[[Stream alloc] init] 
\b openMemory
\b0 ];\
	
\b boP
\b0  = [self 
\b positionFromLine
\b0 :[self 
\b lineFromPosition
\b0 :
\b sp0
\b0 .cp]];\
	
\b eoP
\b0  = [self 
\b positionFromLine
\b0 :([self 
\b lineFromPosition
\b0 :
\b spN
\b0 .cp]
\b +1)
\b0 ];\
	if (
\b eoP
\b0  
\b <
\b0  
\b boP
\b0 ) eoP = [self 
\b textLength
\b0 ];\
	[self 
\b writeETF
\b0 :[
\b undoStream
\b0  stream] 
\b from
\b0 :
\b boP
\b0  
\b to
\b0 :
\b eoP
\b0 ];\
 \
 	[undoManager 
\b setActionName
\b0 :
\b NXUniqueString
\b0 (actionName ? actionName : "")];\
	[undoManager 
\b beginUndoRecordGrouping
\b0 ];\
	return self;\
\}\
- 
\b undoAffectedRange
\b0 :(
\b int
\b0 )from 
\b to
\b0 :(
\b int
\b0 )to \{ // 
\i Call 
\b after
\b0  transaction\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\i0\fc0\cf0 	[[[undoManager setUndoTarget:self] freeUndoArgs] \
			
\b replaceSelWith
\b0 :
\b undoStream
\b0  from:
\b from
\b0  to:
\b to
\b0 ];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0   	[undoManager 
\b endUndoRecordGrouping
\b0 ];\
	
\b undoStream
\b0  = NULL;\
	[self 
\b touch
\b0 ];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\fc0\cf0 	return self;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \}\
\
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b Undo Stream Operations\

\b0 //\
\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\fc0\cf0 - 
\b replaceSelWith
\b0 :
\i stream
\i0  
\b from
\b0 :(int)
\i from
\i0  
\b to
\b0 :(int)
\i to
\i0  \{\
	
\fc1\cf1 [self 
\b setSel
\b0 :from :to];
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0 \

\b0 	[self 
\b undoSelChange
\b0 :[undoManager 
\b currentActionName
\b0 ]];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\gray300\fc2\cf2 	// Replace the given range with the rtf in the given stream\

\gray0\fc0\cf0 	[stream 
\b rewind
\b0 ];
\fc1\cf1 \
	if(stream && [stream stream]) \
		[self 
\b replaceSelWithRichText
\b0 :[
\b stream
\b0  stream]];\
	else [self 
\b replaceSel
\b0 :
\b NULL
\b0 ];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0  
\b0\fc1\cf1 	if (sp0.cp < 0) [self setSel:0:0];\

\b\fc0\cf0  	[self undoAffectedRange:from to:spN.cp];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\b0\fc0\cf0 	return self;\
\}\
- 
\b replaceSel
\b0 :(const char *)
\i str
\i0  
\b from
\b0 :(int)
\i from
\i0  
\b to
\b0 :(int)
\i to
\i0  \{\
	
\fc1\cf1 [self 
\b setSel
\b0 :from :to];
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0 \

\b0 	[self 
\b undoSelChange
\b0 :[undoManager 
\b currentActionName
\b0 ]];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\gray300\fc2\cf2 	// Replace the given range with the rtf in the given stream\

\gray0\fc1\cf1 	if(
\b str
\b0 ) [self 
\b replaceSel
\b0 :
\b str
\b0 ];\
	else [self 
\b replaceSel
\b0 :
\b ""
\b0 ];\
	if (sp0.cp < 0) [self setSel:0:0];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0   	[self undoAffectedRange:from to:spN.cp];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\b0\fc0\cf0 	return self;\
\}\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 \
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b Selection Format Changes\

\b0 //\
- 
\b changeFont
\b0 :sender \{\
	id retval;\
\
	[self 
\b touch
\b0 ];\
	if (sp0.cp == spN.cp) //
\i  If the font is at a caret, just go on.
\i0 \
		return [super 
\b changeFont
\b0 :sender];\
\
	[self 
\b undoSelChange
\b0 :"
\b Change Font
\b0 "];\
	if (![
\b sender
\b0  
\b respondsTo
\b0 :@selector(
\b convertFont
\b0 :)])\
		
\b sender
\b0  = [
\b FontManager
\b0  new];\
	retval = [super 
\b changeFont
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
- 
\b pasteFont
\b0 :sender \{\
	id retval;\
	[self 
\b undoSelChange
\b0 :"
\b Paste Font
\b0 "];\
	retval = [super 
\b pasteFont
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
- 
\b underline
\b0 :sender \{\
	id retval;\
	[self 
\b undoSelChange
\b0 :"
\b Underline
\b0 "];\
	retval = [super 
\b underline
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
- 
\b subscript
\b0 :sender \{\
	id retval;\
\
	[self 
\b undoSelChange
\b0 :"
\b Subscript
\b0 "];\
	retval = [super 
\b subscript
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b superscript
\b0 :sender \{\
	id retval;\
\
	[self 
\b undoSelChange
\b0 :"
\b Superscript
\b0 "];\
	retval = [super 
\b superscript
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b unscript
\b0 :sender \{\
	id retval;\
	[self 
\b undoSelChange
\b0 :"
\b Unscript
\b0 "];\
	retval = [super 
\b unscript
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
- 
\b setSelFont
\b0 :font \{\
	id retval,newF;\
\
	[self 
\b touch
\b0 ];\
	if ([font matrix] != NX_FLIPPEDMATRIX)\
		newF = [Font newFont:[font name] size:[font pointSize]\
						matrix:NX_FLIPPEDMATRIX];\
	else newF = font;\
\
	if (sp0.cp == spN.cp) //
\i  If the font is at a caret, just go on.
\i0 \
		return [super 
\b setSelFont
\b0 :newF];\
\
	[self 
\b undoSelChange
\b0 :"
\b Set Font
\b0 "];\
	retval = [super 
\b setSelFont
\b0 :newF];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b setSelFontSize
\b0 :(
\b float
\b0 )size \{\
	id retval;\
\
	[self 
\b undoSelChange
\b0 :"
\b Set Font Size
\b0 "];\
	retval = [super 
\b setSelFontSize
\b0 :size];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b setSelFontFamily
\b0 :(const char *)fontName \{\
	id retval;\
\
	[self 
\b undoSelChange
\b0 :"
\b Set Font Family
\b0 "];\
	retval = [super 
\b setSelFontFamily
\b0 :fontName];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
- 
\b setSelGray
\b0 :(
\b float
\b0 )value \{\
	id retval;\
	\
	[self 
\b touch
\b0 ];\
	if (sp0.cp == spN.cp) //
\i  If the color is at a caret, just go on.\

\i0 		return [super 
\b setSelGray
\b0 :value];\
	\
	[self 
\b undoSelChange
\b0 :"
\b Set Text Gray
\b0 "];\
	retval = [super 
\b setSelGray
\b0 :value];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b setSelColor
\b0 :(
\b NXColor
\b0 )color \{\
	id retval;\
	\
	[self 
\b touch
\b0 ];\
	if (sp0.cp == spN.cp) //
\i  If the color is at a caret, just go on.
\i0 \
		return [super 
\b setSelColor
\b0 :color];\
\
	[self 
\b undoSelChange
\b0 :"
\b Set Text Color
\b0 "];\
	retval = [super 
\b setSelColor
\b0 :color];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b Paragraph Changes\

\b0 //\
- 
\b pasteRuler
\b0 :sender \{\
	id retval;\
	[self 
\b undoParChange
\b0 :"
\b Paste Ruler
\b0 "];\
	retval = [super 
\b pasteRuler
\b0 :sender];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b changeTabStopAt
\b0 :(
\b NXCoord
\b0 )oldX 
\b to
\b0 :(
\b NXCoord
\b0 )newX \{\
	id retval;\
	[self 
\b undoParChange
\b0 :"
\b Move Tab Stop
\b0 "];\
	retval = [super 
\b changeTabStopAt
\b0 :oldX 
\b to
\b0 :newX];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
- 
\b setSelProp
\b0 :(
\b NXParagraphProp
\b0 )prop 
\b to
\b0 :(
\b NXCoord
\b0 )val \{\
	id retval;\
	[self 
\b undoParChange
\b0 :"
\b Change Indentation
\b0 "];\
	retval = [super 
\b setSelProp
\b0 :prop 
\b to
\b0 :val];\
  	[self 
\b undoAffectedRange
\b0 :
\b sp0
\b0 .cp to:
\b spN
\b0 .cp];\
	return retval;\
\}\
\
//ÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐÐ\
//	
\b UndoManager Delegate\

\b0 //\
- 
\b undoManagerWillUndo
\b0 :sender \{ \
	return [self 
\b registerLastTypingUndo
\b0 ]; \}\
- 
\b registerLastTypingUndo
\b0  \{\
	if(
\b isTyping
\b0 ) \{\
		if ((undoFirstPos != -1) &&(undoFirstPos != undoLastPos)) \{\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\fc1\cf1 			
\fc0\cf0 [[undoManager setUndoTarget:self] \

\b 				replaceSel:"" from:
\b0 undoFirstPos
\b  to:
\b0 undoLastPos
\b ];
\b0 \
		\}
\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 		
\b \
		[undoManager setActionName:"Typing"];\
		[undoManager endUndoRecordGrouping];\

\b0 		undoFirstPos = undoLastPos = -1;\
	\}\
	isTyping = NO;\
	return self;\
\}\

\i \
@end\

\i0 \
char *
\b undoFilterFunc
\b0 (eText *
\b self
\b0 , unsigned char *
\b text
\b0 , int *
\b len
\b0 , int 
\b position
\b0 )\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\fc0\cf0 \{\
	int start = self->sp0.cp, end = self->spN.cp;\
	id undoManager = self->undoManager;\
	\
	if(oldTextFilter) 
\b text
\b0  = (unsigned char *)(*
\b oldTextFilter
\b0 )\
		(self, text, len, position);\

\gray300\fc2\cf2 \

\gray0\fc0\cf0 	if (isTyping && 
\b !
\b0 ((
\b end == undoLastPos
\b0 ) || (
\b start
\b0  == 
\b undoFirstPos
\b0 )))\{\
	
\gray300\fc2\cf2 // If we are typing, and this is not a contiguous extension of the last\
	// typing, then close the previous group\

\gray0\fc0\cf0 		[self 
\b registerLastTypingUndo
\b0 ];\
		isTyping = NO; 
\gray300\fc2\cf2 // This chains into the code immediately below
\gray0\fc0\cf0 \
	\}\

\gray300\fc2\cf2 	// If we're not typing yet, get cranking\

\gray0\fc0\cf0 	if (!isTyping) \{\
		isTyping = YES;\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0 		[undoManager setActionName:"Typing"];\
		[undoManager beginUndoRecordGrouping];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\b0\fc0\cf0 	\}\
\

\gray300\fc2\cf2 	// This should never get called,since nuking one character already moves sp\

\gray0\fc1\cf1 	//if ((start == end) && (text && (*text 
\b == DELETE_KEY
\b0 )))\{\

\b 	//	[self setSel:start-1 :end];\

\fc0\cf0 	//	start = self->sp0.cp, end = self->spN.cp;\

\b0\fc1\cf1 	//\}\

\fc0\cf0 	
\gray300\fc2\cf2 // No matter what, we're about to nuke a section of text.\
	// Note that we generate full RTF code for individual characters.\
	// Jeff's old code avoided that by snapshotting the _entire_ document...\

\gray0\fc0\cf0 	if 
\fc1\cf1 (
\b start != end
\b0 ) \{\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\fc0\cf0 		id stream = [[[Stream alloc] init] openMemory];\
		[self 
\b writeETF
\b0 :[stream 
\b stream
\b0 ] 
\b from
\b0 :start 
\b to
\b0 :end];\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\fc0\cf0 		[[[undoManager setUndoTarget:self] freeUndoArgs] \
			
\b replaceSelWith
\b0 :
\b stream
\b0  from:
\b start
\b0  to:
\b start
\b0 ];\

\pard\tx520\tx1060\tx1600\tx2120\tx2660\tx3200\tx3720\tx4260\tx4800\tx5320\b\fc0\cf0 		\
		//[self replaceSel:""]; 
\b0\gray300\fc2\cf2 // could use objc_msgSendSuper() here\

\b\gray0\fc0\cf0 		undoLastPos = start;\
		undoFirstPos = start;\

\pard\tx540\tx1080\tx1620\tx2160\tx2700\tx3240\tx3780\tx4320\tx4860\tx5400\tx5940\tx6480\tx7020\tx7560\tx8100\tx8640\tx9180\tx9720\tx10260\tx10800\tx11340\b0\fc1\cf1 	\}\
\
	
\gray300\fc2\cf2 // if it was a delete key, we're done; otherwise we have to undo\
	// the subsequent 
\i addition
\i0  of keystrokes
\gray0\fc0\cf0 \

\fc1\cf1 	if (text && (*text 
\b !=
\b0  
\b DELETE_KEY
\b0 )) \{\
		
\fc0\cf0 [[undoManager setUndoTarget:self] \

\b 			replaceSel:"" from:position to:((position + *len))];
\fc1\cf1 \

\fc0\cf0 		undoLastPos = position + *len;\

\b0 		if (
\b undoFirstPos
\b0  == 
\b -1
\b0 ) \
			
\b undoFirstPos
\b0  = 
\b position
\b0 ;\

\fc1\cf1 	\}\

\fc0\cf0     return ((char *)text);\
\}
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.