'From Squeak3.2 of 15 January 2002 [latest update: #4881] on 18 February 2003 at 11:07:41 pm'!"Change Set:		telnetDate:			16 October 2002Author:			Ian PiumartaThis is a local and remote login client for Squeak comprising a protocol stack abstraction, endpoints for network (socket) and subprocess (pseudo tty) communication, a DFSA compiler (for taking the pain out of building stateful protocols), a partial-but-sufficient implementation of the RFC 854 telnet protocol, a glass teletype morph, an essentially complete VT102 terminal emulator (it passes the VT100/102 validation suite, except for wide and double-height characters and application keypad mode [trivial to implement, but I haven't got a keypad to test it with... ;-]) and the vast majority of an xterm emulator.  It comes with a class called PseudoTTY that implements the image side of Unix98 pseudo ttys (allowing the emulator to be connected to a subprocess that in turn believes itself to be connected to a real terminal -- can you say ``login shell''? ;-).  It's also turned into something of a `clickable' application: to fire it up, evaluate	TeletypeWindow openin a Workspace (or stick it in your `common expressions' menu) and then click on the window menu icon to start a shell or telnet session.The terminal emulator now comes with its own font, a *real* TTY font that is infinitely more readable than Atlanta (and which has underscores and all the special graphics/line-drawing characters in all the right places).  It also supports colour correctly: adventurous GNU/Linux users might even like to try running `kbdconfig' in it (and comparing the result to what they see in a real xterm... <grin>!! ;-).  The only significant thing left to do is selection handling, copy and paste.The telnet protocol has been tested with the GNU/Linux, Digital Unix (OSF/1) and Solaris telnet servers.  It requires only the standard Socket support to work and so should be FULLY FUNCTIONAL on Mac and Windows (assuming they implement the SO_OOBINLINE option in Socket>>setOption:value:).The shell has been tested with bash and ash (the closest thing I have to a SysV shell) on GNU/Linux and Solaris (but there's no reason why it shouldn't work on any OS that supports Unix98 [aka XPG5 aka SUSv2] style PTYs).  It requires both AsynchFile and PseudoTTY plugins to work.  This means that the shell only works on Unix for the moment, until somebody gets round to porting the PseudoTTYPlugin to Windows and (ha ha) Mac.Comments, suggestions and bug reports are welcome: ian.piumarta@inria.frEnjoy!!(This changeset was supported by a generous quantity of Jaques Vabre coffee, thoroughly antisocial levels of Sergent Garcia's ``Un poquito quema'o'' and way too many Chesterfield Lights.  ¡Si no sanas hoy, sanarás mañana!!)"!OrderedCollection subclass: #LayeredProtocol	instanceVariableNames: 'properties '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!LayeredProtocol commentStamp: '<historical>' prior: 0!I am a complete protocol implemented as a stack of subprotocols.  One end of the stack (the bottom or `tail') is usually an endpoint communicating with some remote host or process.  The other end (the top or `head') is either the point of communication for my client (which reads and writes only application-oriented data) or even the client itself.I am created by sending my class the message #on: aSubProtocol.  You can then send me #push: aProtocol as many times as you like, to push aProtocol onto the head of my protocol stack.  When the stack is complete you must send me #install which tells me to finish creating the internal connections between each subprotocol in the stack.  Finally you send me #run which tells the entire protocol stack to start.!LayeredProtocol class	instanceVariableNames: ''!Object subclass: #ProtocolLayer	instanceVariableNames: 'session up down '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!ProtocolLayer commentStamp: '<historical>' prior: 0!I am a single layer in a LayeredProtocol stack.  I pass information up and down the stack, possibly transforming it in the process.Structure:	down		(ProtocolLayer) My low protocol, one element closer to the "remote connection" end of the stack.	up			(ProtocolLayer) My high protocol, one element closer to the user interface or other "local client".	session		(LayeredProtocol) The entire collection of ProtocolLayers of which I am one.!ProtocolLayer subclass: #ProtocolAdaptor	instanceVariableNames: 'upBlock downBlock flushBlock noteBlock '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Protocols'!!ProtocolAdaptor commentStamp: '<historical>' prior: 0!I am a pluggable ProtocolLayer.  You can insert me anywhere in a LayeredProtocol stack.Communication between protocol stack layers is accomplished using the following messages:	upcall: datum					-- receive data from the protocol below me in the stack	downcall: datum					-- receive data from the protocol above me	flush							-- the protocol below me might become idle for a while	note: aSymbol with: anObject	-- I am being informed that something "global" has happenedBy default I am completely transparent.  In other words I react to the above messages as follows:	upcall: datum					-- I pass datum on to the protocol above me	downcall: dataum				-- I pass datum on to the protocol below me	flush							-- I pass the message to the protocol above me	note: sym with: obj				-- is ignored entirelyAny or all of these default reactions can be changed by installing blocks which I will execute in response to the above messages.  You install such blocks by sending me the following messages:	upBlock: unaryBlock			-- evaluated on #up: passing datum as argument	downBlock: unaryBlock			-- evaluated on #down: passing datum as argument	flushBlock: aBlock				-- evaulated on #flush with no arguments	noteBlock: binaryBlock			-- evaulated on #note:with: passing aSym and anObj as argumentsBy now you've probably guess that my default behaviour is simply to install the following blocks when I am created:	upBlock: [:datum | up upcall: datum]	downBlock: [:datum | down downcall: datum]	flushBlock: []	noteBlock: [:aSymbol :anObject | ]My class knows how to instantiate particular kinds of default behaviour in me, including:	pass							-- the default (transparency)	trace							-- prints each datum on the Transcript as it whizzes by	reflect							-- bounces downward data back up the stack and vice-versaHere's one example, possibly the shortest known means to create an "echo" server:	(NetworkEndpoint socket: anAcceptedSocket) asProtocolStack		push: ProtocolAdaptor reflect;		install;		run!ProtocolAdaptor class	instanceVariableNames: ''!ProtocolLayer subclass: #ProtocolEndpoint	instanceVariableNames: 'serverProcess '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Endpoints'!!ProtocolEndpoint commentStamp: '<historical>' prior: 0!I am an abstract endpoint for communication within a LayeredProtocol stack.  (I therefore expect to be the lowest element in that stack.)  I implement a server which waits for incoming data and then passes it up the stack for processing by higher protocol layers.  The actual reading of data from the remote entity must be implemented by my concrete subclasses.!ProtocolEndpoint subclass: #NetworkEndpoint	instanceVariableNames: 'socket '	classVariableNames: 'InstanceList '	poolDictionaries: ''	category: 'Communications-Endpoints'!!NetworkEndpoint commentStamp: '<historical>' prior: 0!I am an endpoint for network communication.  I am also a ProtocolLayer and I therefore expect to be inserted as the lowest element in a LayeredProtocol stack.Structure:	socket			(Socket)	-- the socket on which I communicate.!NetworkEndpoint class	instanceVariableNames: ''!ProtocolEndpoint subclass: #ProcessEndpoint	instanceVariableNames: 'pty command arguments '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Endpoints'!!ProcessEndpoint commentStamp: '<historical>' prior: 0!I am an endpoint for communication with another process.  I am also a ProtocolLayer and I therefore expect to be inserted as the lowest element in a LayeredProtocol stack.Well, that's the official story anyway.  In fact I am happy to communicate with anything that talks through an AsyncFile.  It just so happens that one kind of AsyncFile is PsuedoTTY which can be connected to the stdin, stdout and stderr of a remote process.Structure:	pty			(PseudoTTY)	-- the asynchronous file with which I communicate.!ProcessEndpoint class	instanceVariableNames: ''!IdentityDictionary subclass: #ProtocolState	instanceVariableNames: 'name default '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!ProtocolState commentStamp: '<historical>' prior: 0!I am a single state within a cyclic graph of states.  My values are edges leading to another state in the graph.  If the edge has an action associated with it then I perform the method of that name in my client object, passing the object which stepped me as argument, before following the edge.Structure: name		Symbol				-- my state's name keys		Object				-- the input tokens that cause me to step values		#(Symbol1 Symbol2)	-- an edge: the next state and a client action selector default		#(Symbol1 Symbol2)	-- the edge I follow if no key matches the stepping objectI am intended to be inserted somewhere in the middle of a LayeredProtocol stack.!ProtocolState class	instanceVariableNames: ''!Object subclass: #ProtocolStateTransition	instanceVariableNames: 'state action '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!ProtocolStateTransition commentStamp: '<historical>' prior: 0!I am a transition to a new ProtocolState.  While making the transition I can perform some action association with the change from the old to the new state.!ProtocolStateTransition class	instanceVariableNames: ''!Socket subclass: #SafeSocket	instanceVariableNames: 'hostName '	classVariableNames: 'InstanceList '	poolDictionaries: ''	category: 'Communications-Endpoints'!!SafeSocket commentStamp: '<historical>' prior: 0!I am a Socket that knows about the dangers of remaining open across snapshot.!SafeSocket class	instanceVariableNames: ''!Morph subclass: #SimpleTextMorph	instanceVariableNames: 'font fgMap bgMap pitch rv lineState cursorColour '	classVariableNames: 'Background Background2 DefaultStyle Foreground Foreground2 '	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!!SimpleTextMorph commentStamp: '<historical>' prior: 0!I display a string with optional foreground, background and emphasis changes.  I shouldn't really exist but StringMorph doesn't know how to change colour or emphasis in mid-run and cannot affect its background colour at all.  On the other hand, TextMorph does way too much and wants to take over keyboard and mouse input and a whole bunch of other useless stuff such as line wrapping.  I could have used Text as my underlying representation and reused TextMorph's scanner except that it works on paragraphs (not linear text) and can't change background colour anyway.  Ho hum.  Short of installing a whole bunch of new rendering methods in the various canvases to plass explicit background colour information, I guess I just have to do everything myself.  C'est la vie, I suppose.	SimpleTextMorph exampleStructure:	font		StrikeFont		-- the font I render with (this had better be monospaced!!)	pitch		SmallInteger	-- the width of a character in font	string		String			-- my contents	fgRuns		Array			-- foreground changes (should be RunArray)	bgRuns		Array			-- background changes (ditto)	emRuns		Array			-- emphasis changes (ditto)!SimpleTextMorph class	instanceVariableNames: ''!Object subclass: #SimpleTextState	instanceVariableNames: 'string stringSize fgRuns bgRuns emRuns cursorCol changed selection lastCol '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!SimpleTextState class	instanceVariableNames: ''!ProtocolLayer subclass: #StatefulProtocol	instanceVariableNames: 'currentState client '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!StatefulProtocol commentStamp: '<historical>' prior: 0!I am a ProtocolLayer.  I implement my protocol as a state machine, transitioning from one state to another according to patterns that I recognise in the data flowing through me.!StatefulProtocol class	instanceVariableNames: ''!IdentityDictionary subclass: #StatefulProtocolDescription	instanceVariableNames: 'initialState '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!StatefulProtocolDescription commentStamp: '<historical>' prior: 0!I am a collection of ProtocolStates constituting a transition graph for a StatefulProtocol.  See my class side for some examples of how I construct state machine descriptions for you.Note that before I can be used to drive a StatefulProtocol you *must* send me #compile.  I will answer the initial ProtocolState in the compiled transition graph.  (I will also complain if your protocol is broken. ;-)  You subsequently pass this ProtocolState as the argument to StatefulProtocol class>>initialState: in order to instantiate a new StatefulProtocol.Structure: initialState		Symbol	-- the name of the initial (root) node in my transition graph!StatefulProtocolDescription class	instanceVariableNames: ''!StatefulProtocol subclass: #StatefulProtocolTester	instanceVariableNames: 'prefix '	classVariableNames: 'States '	poolDictionaries: ''	category: 'Communications-Abstract'!!StatefulProtocolTester commentStamp: '<historical>' prior: 0!I am a simple (but complete) state machine.  I recognise sequences of characters (with embedded numeric arguments for certain sequences) in strings.  I also print out what I'm doing on the Transcript so that you can see precisely how I work.	StatefulProtocolTester testStructure: prefix		SmallInteger	-- the numeric argument currently under contruction!StatefulProtocolTester class	instanceVariableNames: ''!StatefulProtocolTester subclass: #StatefulProtocolTester2	instanceVariableNames: 'e x xy xyz '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Abstract'!!StatefulProtocolTester2 commentStamp: '<historical>' prior: 0!I am just like StatefulProtocolTester except that I measure the throughput of the protocol as number of state transitions per second.	StatefulProtocolTester2 test!StatefulProtocolTester2 class	instanceVariableNames: ''!RectangleMorph subclass: #TeletypeMorph	instanceVariableNames: 'inset font pitch skip rows cols lines savedLines savedLineLimit displayStart topLine bottomLine down x y fg bg em rv ec tabs useScrollbar scroll scrollFlop scrollRight scrollOn autoWrap reverseWrap autoLinefeed autoCR relativeOrigin insertMode showCursor session systemWindow running autoFlush smoothScroll steps metaSendsEscape deleteIsDel altScreenSwitch altScreenActive altScreenColours reverseVideo hasFocus mousePosition selectionStart selectionEnd selectionActive selection trackingSelection mouseControlsSelection keyboardControlsSelection scrollOnInput scrollOnOutput allow132 characterClasses cursorColour '	classVariableNames: 'CharClass KeyboardControlsSelection MouseControlsSelection SaveTerminalSize SavedLineLimit TextCursor '	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!!TeletypeMorph commentStamp: '<historical>' prior: 0!I am a glass teletype.  I accept raw input from some source (which I display without interpretation) and generate raw characters for some sink.I implement sufficient cursor addressing and character attributes to provide terminal emulators with the necessary support to implement ANSI (ISO 6429) colours and emphasis (with the exception of blinking) and the full range of VT220-style cursor-based screen editing.  However, I make no attempt to interpret ASCII control characters nor ANSI, DEC (or any other) escape sequences.  This, like all other `cooked' interpratation of special characters, is left entirely to my source and sink (which will normally be the same instance of some terminal emulator, immediately below me in a protocol stack).I am designed to be the head of a protocol stack.  For this reason I expect my source to send me #upcall: aCharacter (I am the high protocol for some emulator) and I pass keyboard events down to my sink by sending it #downcall: aCharacter (the sink is my low protocol).  Anybody can send me #delete which I will propagate to all members of my protocol stack as a #windowClosed note.  (This will normally cause any connected endpoints at the tail end of the stack to be disconnected and destroyed.)	TeletypeMorph new openInWorld!TeletypeMorph class	instanceVariableNames: ''!SystemWindow subclass: #TeletypeWindow	instanceVariableNames: 'tty windowTitle iconTitle '	classVariableNames: 'AutoClose SaveTelnetOptions SaveTerminalMainOptions SaveTerminalSize SaveTerminalVTOptions Shortcuts TerminalType '	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!!TeletypeWindow commentStamp: '<historical>' prior: 0!I am a kind of SystemWindow intended specifically for hosting a TeletypeMorph.  I provide connection-oriented menus, persistent preferences and menu-based access to my TeletypeMorphs options.!TeletypeWindow class	instanceVariableNames: ''!StatefulProtocol subclass: #TelnetProtocol	instanceVariableNames: 'debug dumpNetData dumpTermData doNAWS cols rows '	classVariableNames: 'AO AYT Break Commands DataMark Debug Do Dont DumpNetData DumpTermData EC EL Echo EnvironmentOption ForwardX GoAhead IAC IP Is Linemode Logout NAWS NewEnvironment Nop Options RemoteFlowControl SB SE Send Status Subnegotiation SuppressGoAhead SuppressLocalEcho TerminalSpeed TerminalType Will Wont XDisplayLocation '	poolDictionaries: ''	category: 'Communications-Protocols'!!TelnetProtocol commentStamp: '<historical>' prior: 0!I am a ProtocolLayer implementing the telnet (RFC 854) protocol.  I expect to be inserted into a LayeredProtocol stack in which the endpoint is connected to a remote telnet server.  When I receive #run from the protocol stack I will begin negotiation with the remove server.  If the negotiation is successful, the protocol above me in the stack (or the client of the entire stack) will see an interactive login session.!TelnetProtocol class	instanceVariableNames: 'States '!StatefulProtocol subclass: #VT102Emulator	instanceVariableNames: 'arguments window trace mode keypadMode cursorState charMap g0Map g1Map keyMap '	classVariableNames: 'CharsGR CharsUK CharsUS '	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!!VT102Emulator commentStamp: '<historical>' prior: 0!I emulate a VT102 terminal.  The VT102 is an enhanced VT100 with additional screen editing functions, especially useful for full-screen editors (like Emacs) running over slow connections.I expect to be inserted somewhere in the middle of a protocol stack.  My low procotol (usually some kind of remote login protocol) sends me #upcall: aCharacter to display aCharacter.  I filter out control sequences (sending appropriate messages to my window (usually a TeletypeMorph) to effect cursor movement, character attribute manipulation, screen editing and so on) or simply pass printing characters up to my high protocol (usually the same TeletypeMorph as my window) verbatim for display.  I accept keyboard input from my high protocol (again usually my window) which I pass down to my low protocol with conversion from Squeak keycodes to ANSI or DEC escape sequences representing cursor and other special keys.I'm a subclass of Protocol, which is a subclass of StateMachine, since terminal emulation is simply a matter of recognising particular patterns (`escape sequences') in a stream of characters (as is any kind of `protocol').I recognise the escape sequences as defined in: "VT102 Video Terminal User Guide", 3rd edition, Digital Equipment Corporation, February 1982, part number EK-VT102-UG-003.  When in VT52 mode I also recognise the escape sequences defined in "DECscope User's Manual", 1st edition, Digital Equipment Corporation, April 1976, part number EK-VT5X-OP-001.  (If you don't own copies of these historic documents then Shame On You!! ;-)!VT102Emulator class	instanceVariableNames: 'States '!VT102Emulator subclass: #XtermEmulator	instanceVariableNames: 'savedBuffer '	classVariableNames: ''	poolDictionaries: ''	category: 'Communications-Terminal Emulation'!!XtermEmulator commentStamp: '<historical>' prior: 0!I emulate a xterm terminal.  The xterm is a modern member of the DEC VT200 family with additional ANSI functions including support for ISO 6429 colour escape sequences.  I am particularly useful for full-screen programs that know how to use colour highlighting (such as Emacs version 21 and higher).I inherit from VT102Emulator because I implement a proper superset of its capabilities.See the class comments in my superclasses (VT102Emulator in particular) for further information on how to use me.!XtermEmulator class	instanceVariableNames: ''!!BlockContext methodsFor: 'evaluating'!value: arg1 value: arg2 value: arg3 value: arg4 value: arg5	"Primitive. Evaluate the block represented by the receiver. Fail if the 	block expects other than three arguments or if the block is already being 	executed. Optional. See Object documentation whatIsAPrimitive."	<primitive: 81>	^self valueWithArguments: 		(Array			with: arg1			with: arg2			with: arg3			with: arg4			with: arg5)! !!FormCanvas methodsFor: 'drawing-text'!drawString: aString from: firstIndex to: lastIndex in: bounds font: fontOrNil color: c background: b	| font portRect |	port colorMap: nil.	portRect := port clipRect.	port clipByX1: bounds left + origin x 		y1: bounds top + origin y 		x2: bounds right + origin x 		y2: bounds bottom + origin y.	font := fontOrNil ifNil: [TextStyle defaultFont].	port combinationRule: Form paint.	font installOn: port		foregroundColor: (self shadowColor ifNil:[c]) 		backgroundColor: b.	font displayString: aString asString on: port 		from: firstIndex to: lastIndex at: (bounds topLeft + origin) kern: 0.	port clipRect: portRect.! !!LayeredProtocol methodsFor: 'initialize-release'!initializeProperties	properties := IdentityDictionary new! !!LayeredProtocol methodsFor: 'accessing'!head	"Answer the head of the stack."	^self first! !!LayeredProtocol methodsFor: 'accessing'!pop	"Remove the head of the stack."	^self removeFirst! !!LayeredProtocol methodsFor: 'accessing'!propertyAt: aKey	^properties at: aKey ifAbsent: []! !!LayeredProtocol methodsFor: 'accessing'!propertyAt: aKey ifAbsent: aBlock	^properties at: aKey ifAbsent: aBlock! !!LayeredProtocol methodsFor: 'accessing'!propertyAt: aKey put: aValue	^properties at: aKey put: aValue! !!LayeredProtocol methodsFor: 'accessing'!push: aProto	"Push a new protocol onto the head of the stack."	self addFirst: aProto! !!LayeredProtocol methodsFor: 'accessing'!tail	"Answer the tail of the stack."	^self last! !!LayeredProtocol methodsFor: 'protocol'!endpoint	^self last! !!LayeredProtocol methodsFor: 'protocol'!install	| prev |	prev := nil.	self do: [:this |		this session: self.		prev isNil ifFalse: [this up: prev.  prev down: this].		this install.		prev := this]! !!LayeredProtocol methodsFor: 'protocol'!isConnected	^self endpoint isConnected! !!LayeredProtocol methodsFor: 'protocol'!note: aSymbol with: anObject	"Inform my members that something has happened."	self do: [:proto | proto note: aSymbol with: anObject]! !!LayeredProtocol methodsFor: 'protocol'!run	self do: [:proto | proto run]! !!LayeredProtocol methodsFor: 'private'!addDown: proto	^self addLast: proto! !!LayeredProtocol methodsFor: 'private'!addUp: proto	^self addFirst: proto! !!LayeredProtocol methodsFor: 'private'!removeUp	^self removeFirst! !!LayeredProtocol class methodsFor: 'instance creation'!new	^super new initializeProperties! !!LayeredProtocol class methodsFor: 'instance creation'!on: proto	^self new push: proto! !!LayeredProtocol class methodsFor: 'examples'!example	"Send 42 down a stack and then reflect it back up."	"LayeredProtocol example"	(ProtocolAdaptor new reflect asProtocolStack		push: ProtocolAdaptor new trace;		push: ProtocolAdaptor new trace;		push: (ProtocolAdaptor new trace				upBlock: [:arg | Transcript cr; show: 'ping ' , arg printString]);		install;		run;		first) downcall: 42! !!LayeredProtocol class methodsFor: 'examples'!example3	"LayeredProtocol example3"	"Glass teletype with local echo."	^ProtocolAdaptor new localEcho asProtocolStack		push:	TeletypeMorph new openInWorld;		install;		run! !!ProtocolLayer methodsFor: 'accessing'!down: protoLo	down := protoLo! !!ProtocolLayer methodsFor: 'accessing'!session	^session! !!ProtocolLayer methodsFor: 'accessing'!session: aSession	session := aSession! !!ProtocolLayer methodsFor: 'accessing'!up: protoHi	up := protoHi! !!ProtocolLayer methodsFor: 'testing'!isConnected	^false! !!ProtocolLayer methodsFor: 'stack'!asProtocolStack	"Answer a new LayeredProtocol in which I am the lowest element."	^LayeredProtocol on: self! !!ProtocolLayer methodsFor: 'stack'!downcall: anObject	"Receive anObject from my high protocol.  The default behaviour is to pass it down unmodified to my low protocol (i.e., I am transparent)."	down downcall: anObject! !!ProtocolLayer methodsFor: 'stack'!downcallAll: aCollection	"Receive aCollection of data from my high protocol.  The default behaviour is to act as if each datum was received individually."	aCollection do: [:elt | self downcall: elt]! !!ProtocolLayer methodsFor: 'stack'!flush	"Inform my high protocol that we might be about to pause for a while."	up flush! !!ProtocolLayer methodsFor: 'stack'!install	"Perform any additional actions when creating the initial up/down connections between elements in the ProtocolStack."! !!ProtocolLayer methodsFor: 'stack'!passUp: anObject	"Pass anObject up to my high protocol."	up upcall: anObject! !!ProtocolLayer methodsFor: 'stack'!push: aProtocol	"Push aProtocol on top of the receiver."	^(LayeredProtocol on: self) push: aProtocol! !!ProtocolLayer methodsFor: 'stack'!run	"Perform any additional actions when starting the protocol running (i.e., just before data transfer begins)."! !!ProtocolLayer methodsFor: 'stack'!upcall: anObject	"Receive anObject from my low protocol.  Default is to pass it on to my high protocol unmodified (in other words, I am transparent)."	up upcall: anObject! !!ProtocolLayer methodsFor: 'stack'!upcallAll: aCollection	"Receive aCollection of data from my low protocol.  By default I act as if each datum had been received individually."	aCollection do: [:b | self upcall: b]! !!ProtocolLayer methodsFor: 'session'!note: aSymbol with: anArgument	"My session is informing me that something has happened.  By default I don't care about it."! !!ProtocolLayer methodsFor: 'session'!sessionNote: aSymbol	"Tell my session that something has happened.  If I have no session then do nothing."	session isNil ifFalse: [session note: aSymbol with: nil]! !!ProtocolLayer methodsFor: 'session'!sessionNote: aSymbol with: anArgument	"Tell my session that something has happened.  If I have no session then do nothing."	session isNil ifFalse: [session note: aSymbol with: anArgument]! !!ProtocolLayer methodsFor: 'diagnostics'!ttyCR	self ttyMsg: String cr! !!ProtocolLayer methodsFor: 'diagnostics'!ttyMsg: aString	aString withInternetLineEndings asByteArray do: [:b | up upcall: b].	up flush! !!ProtocolLayer methodsFor: 'diagnostics'!ttyMsgCR: aString	self ttyMsg: aString, String cr! !!ProtocolAdaptor methodsFor: 'initialize-release'!initialize	upBlock := [:arg | self notify: 'up ' , arg printString].	downBlock := [:arg | self notify: 'down ' , arg printString].	flushBlock := [].	noteBlock := [:s :a | ]! !!ProtocolAdaptor methodsFor: 'canned protocols'!localEcho	"A reflect suitable for local echo on dumb ttys, when placed immediately below a TtyMorph, recognising a few of the ASCII control characters."	| c |	^self		upBlock: [:arg | down downcall: arg];		downBlock: [:arg | c := arg bitAnd: 127.			(c == 8) | (c == 127) ifTrue: [up bs; upcall: 32; bs]				ifFalse: [(c == 10) | (c == 13) ifTrue: [up newline]				ifFalse: [c >= 32 ifTrue: [up upcall: c]]].			up flush.]! !!ProtocolAdaptor methodsFor: 'canned protocols'!pass	^self		upBlock: [:arg | up upcall: arg];		downBlock: [:arg | down downcall: arg]! !!ProtocolAdaptor methodsFor: 'canned protocols'!reflect	^self		upBlock: [:arg | down downcall: arg];		downBlock: [:arg | up upcall: arg; flush]! !!ProtocolAdaptor methodsFor: 'canned protocols'!trace	^self		upBlock: [:arg |			Transcript cr; nextPutAll: 'up ' , arg printString; endEntry.			up upcall: arg];		downBlock: [:arg |			Transcript cr; nextPutAll: 'down ' , arg printString; endEntry.			down downcall: arg]! !!ProtocolAdaptor methodsFor: 'accessing'!downBlock: block	downBlock := block! !!ProtocolAdaptor methodsFor: 'accessing'!flushBlock: block	flushBlock := block! !!ProtocolAdaptor methodsFor: 'accessing'!noteBlock: block	noteBlock := block! !!ProtocolAdaptor methodsFor: 'accessing'!upBlock: block	upBlock := block! !!ProtocolAdaptor methodsFor: 'protocol'!downcall: arg	downBlock value: arg! !!ProtocolAdaptor methodsFor: 'protocol'!flush	flushBlock value! !!ProtocolAdaptor methodsFor: 'protocol'!note: aSymbol with: anObject	noteBlock value: aSymbol value: anObject! !!ProtocolAdaptor methodsFor: 'protocol'!upcall: arg	upBlock value: arg! !!ProtocolAdaptor class methodsFor: 'instance creation'!new	^super new initialize! !!ProtocolAdaptor class methodsFor: 'instance creation'!upBlock: aBlock	^self new upBlock: aBlock! !!ProtocolEndpoint methodsFor: 'protocol'!close	"Terminate the connection to the remote entity."	^self subclassResponsibility! !!ProtocolEndpoint methodsFor: 'protocol'!downcall: anObject	"Receive anObject from my high protocol.  The default behaviour (in Protocol) is to pass it down to my low protocol.  ProtocolEndpoints however are at the bottom of the ProtocolStack and thus have no low protocol, so they have to treat this specially (e.g., by sending the data over the network or to another process)."	^self subclassResponsibility! !!ProtocolEndpoint methodsFor: 'protocol'!isConnected	"Answer whether the endpoint is still connected."	^self subclassResponsibility! !!ProtocolEndpoint methodsFor: 'protocol'!name	"Answer the name of the remote entity."	^self subclassResponsibility! !!ProtocolEndpoint methodsFor: 'protocol'!note: aSymbol with: anObject	super note: aSymbol with: anObject.	aSymbol == #endpointClosed ifTrue: [^self terminateServer].	aSymbol == #windowClosed ifTrue: [^self close].! !!ProtocolEndpoint methodsFor: 'protocol'!run	"Spawn a background process running the serverLoop."	super run.	serverProcess := [self serverLoop] forkAt: Processor userSchedulingPriority.! !!ProtocolEndpoint methodsFor: 'server loop'!getData	"Answer a collection of data to be passed up the protocol stack, or nil if the endpoint has failed."	^self subclassResponsibility! !!ProtocolEndpoint methodsFor: 'server loop'!serverLoop	"I sit in a loop (usually in a background Process) waiting for data to arrive on my underlying connection then pass anything received up to my high protocol.  When #getData answers nil to inform me that my underlying connection has been destroyed I close down the entire endpoint, inform my session of the fact and then exit.  See also ProtocolEndpoint>>run."	| buf |"MessageTally spyOn: ["	Transcript cr; show: self class name, ' server running'.	[(buf := self getData) notNil] whileTrue: [up upcallAll: buf].	serverProcess := nil.	self sessionNote: #endpointClosed; close.	Transcript cr; show: self class name, ' server terminated'."] toFileNamed: 'spy.out'"! !!ProtocolEndpoint methodsFor: 'server loop'!terminateServer	"Terminate the serverLoop process.  This should never be called directly: use #close instead."	serverProcess isNil		ifFalse:			[serverProcess terminate.			 serverProcess := nil.			 self sessionNote: #disconnected.			 Transcript cr; show: self class name, ' server terminated']! !!NetworkEndpoint methodsFor: 'accessing'!getData	| buf count |	Processor yield.	buf := ByteArray new: 1024.	[socket dataAvailable		ifTrue:			[count := socket receiveDataInto: buf.			 ^buf copyFrom: 1 to: count]		ifFalse:			[up flush.			 socket waitForDataUntil: (Socket deadlineSecs: 1)].	 socket isConnected]		whileTrue.	^nil! !!NetworkEndpoint methodsFor: 'accessing'!isConnected	^socket notNil and: [socket isConnected]! !!NetworkEndpoint methodsFor: 'accessing'!name	"Answer the name of the peer."	(socket isNil or: [socket isConnected not])		ifTrue: [^'not connected'].	^NetNameResolver stringFromAddress: socket remoteAddress! !!NetworkEndpoint methodsFor: 'accessing'!socket	^socket! !!NetworkEndpoint methodsFor: 'protocol stack'!downcall: char	socket isConnected		ifTrue: [socket sendData: (ByteArray with: char)]		ifFalse: [socket closeAndDestroy]! !!NetworkEndpoint methodsFor: 'protocol stack'!downcallAll: collection	"This is just for speed in telnet negotiation."	socket isConnected		ifTrue: [socket sendData: collection]		ifFalse: [socket closeAndDestroy]! !!NetworkEndpoint methodsFor: 'protocol stack'!note: aSymbol with: anObject	super note: aSymbol with: anObject.	aSymbol == #connectedTo		ifTrue: [^self ttyMsgCR: 'Connected to ', anObject, '.'].	aSymbol == #endpointClosed		ifTrue: [^self ttyCR; ttyMsgCR: 'Connection closed.'].	aSymbol == #oobInlineEndpoint	ifTrue: [^socket setOption: 'SO_OOBINLINE' value: 1].	aSymbol == #savePreferences		ifTrue: [^self savePreferences: anObject].! !!NetworkEndpoint methodsFor: 'protocol stack'!run	self isConnected ifTrue: [self sessionNote: #connectedTo with: self name].	super run! !!NetworkEndpoint methodsFor: 'initialize-release'!close	socket notNil ifTrue: [socket close]! !!NetworkEndpoint methodsFor: 'initialize-release'!socket: aSocket	socket := aSocket! !!NetworkEndpoint methodsFor: 'debugging'!rcvd: char	| str |	str := char asInteger printStringBase: 16.	str := str copyFrom: 4 to: str size.	Transcript cr; nextPut: $<; tab; nextPutAll: (str padded: #left to: 2 with: $0); tab; nextPut: char asCharacter! !!NetworkEndpoint methodsFor: 'debugging'!sent: char	| str |	str := char asInteger printStringBase: 16.	str := str copyFrom: 4 to: str size.	Transcript cr; nextPut: $>; tab; nextPutAll: (str padded: #left to: 2 with: $0); tab; nextPut: char asCharacter! !!NetworkEndpoint methodsFor: 'private'!savePreferences: dict	dict at: #endpointClass put: self class name.	dict at: #endpointCreate put: #newConnection:port:.	dict at: #endpointArguments put: (Array with: socket name with: socket remotePort)! !!NetworkEndpoint class methodsFor: 'examples'!example	"NetworkEndpoint example"	^NetworkEndpoint new		socket: ((Socket initializeNetwork; new)					connectTo: (NetNameResolver addressForName: 'localhost') port: 13;					waitForConnectionUntil: (Socket deadlineSecs: 1);					yourself);		up: (ProtocolAdaptor new				upBlock: [:arg | Transcript nextPut: arg asCharacter; endEntry]);		run.! !!NetworkEndpoint class methodsFor: 'examples'!example2	"NetworkEndpoint example2"	| s |	NetworkEndpoint new		socket: ((s := Socket initializeNetwork; new)					connectTo: (NetNameResolver addressForName: 'localhost') port: 7;					waitForConnectionUntil: (Socket deadlineSecs: 1);					yourself);		up: (ProtocolAdaptor new				upBlock: [:arg | Transcript nextPut: arg asCharacter; endEntry]);		run.	s sendData: 'send this back to me'.	(Delay forSeconds: 1) wait.	s close! !!NetworkEndpoint class methodsFor: 'examples'!example3	"NetworkEndpoint example3"	| ep |	(ep := NetworkEndpoint newConnection: 'localhost' port: 7)		up: (ProtocolAdaptor upBlock:				[:arg | Transcript nextPut: arg asCharacter; endEntry]);		run.	ep socket sendData: 'send this back to me'.	(Delay forSeconds: 1) wait.	ep socket close! !!NetworkEndpoint class methodsFor: 'examples'!newConnection: host port: port	| s addr |	Socket initializeNetwork.	(addr := NetNameResolver addressForName: host) isNil		ifTrue: [^nil].	(s := SafeSocket new)		connectTo: addr port: port;		waitForConnectionUntil: Socket standardDeadline.	s isWaitingForConnection		ifTrue: [s destroy. ^nil].	s name: host.	^self new socket: s! !!ProcessEndpoint methodsFor: 'initialize-release'!command: cmd arguments: args	(pty := PseudoTTY command: cmd arguments: args)		isNil ifTrue: [^self error: 'could not create process'].	command := cmd.	arguments := args.! !!ProcessEndpoint methodsFor: 'protocol'!close	pty close.! !!ProcessEndpoint methodsFor: 'protocol'!downcall: char	pty nextPut: char! !!ProcessEndpoint methodsFor: 'protocol'!isConnected	^pty isConnected! !!ProcessEndpoint methodsFor: 'protocol'!name	^pty name! !!ProcessEndpoint methodsFor: 'protocol'!note: aSymbol with: anObject	super note: aSymbol with: anObject.	aSymbol == #windowSize		ifTrue: [^pty noteWindowSize: anObject "Point"].	aSymbol == #endpointClosed	ifTrue: [^self ttyCR; ttyMsgCR: 'Process terminated.'].	aSymbol == #savePreferences	ifTrue: [^self savePreferences: anObject]! !!ProcessEndpoint methodsFor: 'server loop'!getData	"Answer the next chunk of stuff from the pty or nil if the pty has been closed."	| buf |	pty isConnected ifFalse: [^nil].	Processor yield.	(buf := pty peekUpToEnd) isNil ifTrue: [up flush.  buf := pty upToEnd].	^buf! !!ProcessEndpoint methodsFor: 'private'!savePreferences: dict	dict		at: #endpointClass put: self class name;		at: #endpointCreate put: #command:arguments:;		at: #endpointArguments put: (Array with: command with: arguments)! !!ProcessEndpoint class methodsFor: 'instance creation'!command: command	^self command: command arguments: nil! !!ProcessEndpoint class methodsFor: 'instance creation'!command: command arguments: arguments	^self new command: command arguments: arguments! !!ProcessEndpoint class methodsFor: 'instance creation'!example	"ProcessEndpoint example upToEnd asString"	^ProcessEndpoint command: '/bin/bash' arguments: #('-ec' '/bin/pwd')! !!ProcessEndpoint class methodsFor: 'instance creation'!example2	"ProcessEndpoint example2"	| tty |	^LayeredProtocol new		addDown: (tty := TeletypeMorph open);		addDown: (XtermEmulator new window: tty);		addDown: (ProcessEndpoint command: '/bin/bash' arguments: #('-i'));		install;		run! !!ProcessEndpoint class methodsFor: 'instance creation'!example3	"ProcessEndpoint example3"	^(LayeredProtocol		on:		(ProcessEndpoint command: '/bin/bash' arguments: #('-i')))		push:	XtermEmulator new;		push:	TeletypeMorph open;		install;		run! !!ProtocolState methodsFor: 'accessing'!add: anAssociation	^self transitionAt: anAssociation key put: (self transitionFor: anAssociation value)! !!ProtocolState methodsFor: 'accessing'!addAll: anAssociation	^self atAll: anAssociation key put: anAssociation value! !!ProtocolState methodsFor: 'accessing'!addAllInteger: anAssociation	^self atAllInteger: anAssociation key put: anAssociation value! !!ProtocolState methodsFor: 'accessing'!addInteger: anAssociation	^self transitionAt: anAssociation key asInteger put: (self transitionFor: anAssociation value)! !!ProtocolState methodsFor: 'accessing'!at: key put: transition	^self transitionAt: key put: (self transitionFor: transition)! !!ProtocolState methodsFor: 'accessing'!at: anObject to: limit put: transition	| edge |	edge := self transitionFor: transition.	anObject to: limit do: [:target | self transitionAt: target put: edge]! !!ProtocolState methodsFor: 'accessing'!atAll: collection put: transition	| edge |	edge := self transitionFor: transition.	collection do: [:elt | self transitionAt: elt put: edge]! !!ProtocolState methodsFor: 'accessing'!atAllInteger: collection put: transition	| edge |	edge := self transitionFor: transition.	collection do: [:elt | self transitionAt: elt asInteger put: edge]! !!ProtocolState methodsFor: 'accessing'!default	^default! !!ProtocolState methodsFor: 'accessing'!default: transition	self defaultTransition: (self transitionFor: transition)! !!ProtocolState methodsFor: 'accessing'!defaultTransition: aTransition	default := aTransition! !!ProtocolState methodsFor: 'accessing'!name	^name! !!ProtocolState methodsFor: 'accessing'!name: aSymbol	name := aSymbol! !!ProtocolState methodsFor: 'accessing'!transitionAt: key	^super at: key ifAbsent: [default]! !!ProtocolState methodsFor: 'accessing'!transitionAt: key put: edge	^super at: key put: edge! !!ProtocolState methodsFor: 'printing'!printElementsOn: aStream	aStream nextPutAll: '(name: ' , name printString.	aStream nextPutAll: ' default: ' , default printString.	aStream nextPutAll: ' transitions:'.	self associationsDo: [:transition | aStream space.  transition printOn: aStream.].	aStream nextPut: $).! !!ProtocolState methodsFor: 'private'!transitionFor: transition	^ProtocolStateTransition action: transition key state: transition value! !!ProtocolState class methodsFor: 'instance creation'!name: myName default: aTransition	^self new		name: myName;		default: aTransition! !!ProtocolState class methodsFor: 'examples'!example	"ProtocolState example"	^(self name: #initial default: #echo: -> #initial)		at: 42 put: #echo42: -> #initial;		yourself! !!ProtocolStateTransition methodsFor: 'accessing'!action	^action! !!ProtocolStateTransition methodsFor: 'accessing'!action: aSymbol	action := aSymbol! !!ProtocolStateTransition methodsFor: 'accessing'!state	^state! !!ProtocolStateTransition methodsFor: 'accessing'!state: aState	state := aState! !!ProtocolStateTransition methodsFor: 'state transitions'!transitionFrom: lastState for: aClient with: anObject	action isNil ifFalse: [aClient perform: action with: anObject].	^state isNil ifTrue: [lastState] ifFalse: [state]! !!ProtocolStateTransition methodsFor: 'printing'!printOn: aStream	aStream nextPut: ${.	action printOn: aStream.	aStream nextPutAll: ' -> '.	aStream nextPutAll: ((state isMemberOf: Symbol)		ifTrue: [state]		ifFalse: [state name]) printString.	aStream nextPut: $}! !!ProtocolStateTransition class methodsFor: 'instance creation'!action: aSymbol	^self new state: nil; action: aSymbol! !!ProtocolStateTransition class methodsFor: 'instance creation'!action: aSymbol state: aState	^self new state: aState; action: aSymbol! !!ProtocolStateTransition class methodsFor: 'instance creation'!state: aState	^self new state: aState; action: nil! !!ProtocolStateTransition class methodsFor: 'instance creation'!state: aState action: aSymbol	^self new state: aState; action: aSymbol! !!SafeSocket methodsFor: 'initialize-release'!close	super close.	InstanceList remove: self ifAbsent: []! !!SafeSocket methodsFor: 'initialize-release'!connectTo: host port: port	hostName := host.	^super connectTo: hostName port: port! !!SafeSocket methodsFor: 'accessing'!name	"Answer the name of the peer."	^self isConnected		ifTrue: [hostName isNil ifTrue: [NetNameResolver stringFromAddress: self remoteAddress] ifFalse: [hostName]]		ifFalse: ['not connected']! !!SafeSocket methodsFor: 'accessing'!name: aString	"Set the name of the connection."	hostName := aString! !!SafeSocket methodsFor: 'primitives'!primSocketCloseConnection: socketID	"Close the connection on the given port. The remote end is informed that this end has closed and will do no further sends. This is an asynchronous call; query the socket status to discover if and when the connection is actually closed.  Overriden to avoid primitive fail error in superclass."	<primitive: 'primitiveSocketCloseConnection' module: 'SocketPlugin'>! !!SafeSocket methodsFor: 'primitives'!primSocketReceiveDataAvailable: socketID	"Return true if data may be available for reading from the current socket.  Overridden to avoid primitive failure when the socket is closed asynchronously (or left open across snapshot and quit)."	<primitive: 'primitiveSocketReceiveDataAvailable' module: 'SocketPlugin'>	^false! !!SafeSocket class methodsFor: 'class initialization'!initialize	"SafeSocket initialize"	InstanceList := IdentitySet new.	Smalltalk		addToStartUpList: self;		addToShutDownList: self.! !!SafeSocket class methodsFor: 'instance creation'!new	^InstanceList add: super new! !!SafeSocket class methodsFor: 'snapshot'!shutDown: quitting	"We're about to snapshot and quit: shut down any open connections."	InstanceList := InstanceList select: [ :sock | sock isConnected].	(quitting and: [InstanceList notEmpty]) ifTrue:		[(self confirm: (String streamContents: [ :str |				str nextPutAll: 'Open network connections exist to the following hosts:'.				InstanceList do: [ :sock | str cr; tab; nextPutAll: sock name].				str cr; nextPutAll: 'Do you want to shut them down before quitting?']))			ifTrue: [InstanceList do: [:sock | sock close]]]! !!SafeSocket class methodsFor: 'snapshot'!startUp: resuming	"We're coming back from snapshot and quit.  Close any connections that were left open in the snapshot."	(resuming and: [InstanceList notEmpty])		ifTrue: [InstanceList do: [ :sock | sock close]]! !!SimpleTextMorph methodsFor: 'initialize-release'!contents: aLineState	"Initialize the receiver with the given contents."	super initialize.	font := self defaultFont.	fgMap := Foreground shallowCopy.	bgMap := Background shallowCopy.	lineState := aLineState.	pitch := font widthOfString: ' '.	color := Color white.	rv := false.	cursorColour := nil.! !!SimpleTextMorph methodsFor: 'initialize-release'!initialize	"Initialize the receiver with empty contents."	self contents: SimpleTextState new.! !!SimpleTextMorph methodsFor: 'accessing'!cursorColour: cc	cursorColour := cc.! !!SimpleTextMorph methodsFor: 'accessing'!ec: aBoolean	aBoolean ifTrue: [self emacsColours] ifFalse: [self normalColours]! !!SimpleTextMorph methodsFor: 'accessing'!emacsColours	fgMap := Foreground2 shallowCopy.	bgMap := Background2 shallowCopy.	rv ifTrue: [self reversePolarity]! !!SimpleTextMorph methodsFor: 'accessing'!lineState	"Answer an opaque representation of the line contents"	^lineState! !!SimpleTextMorph methodsFor: 'accessing'!lineState: state	"Restore the line contents from the given state"	lineState := state.	state changed: true.		"force redraw on next cycle"! !!SimpleTextMorph methodsFor: 'accessing'!normalColours	fgMap := Foreground shallowCopy.	bgMap := Background shallowCopy.	rv ifTrue: [self reversePolarity]! !!SimpleTextMorph methodsFor: 'accessing'!rv: aBoolean	aBoolean ~~ rv		ifTrue:			[rv := aBoolean.			 self reversePolarity]! !!SimpleTextMorph methodsFor: 'drawing'!drawCursorOn: aCanvas	| cc bg fg |	lineState cursorColumn > 0 ifTrue:		[cc := lineState cursorColumn min: lineState stringSize.		 (lineState selectionSpansColumn:  cc)			ifTrue:				[fg := self backgroundColourAt: (lineState bgRuns at: cc).				 bg := self foregroundColourAt: (lineState fgRuns at: cc)]			ifFalse:				[bg := self backgroundColourAt: (lineState bgRuns at: cc).				 fg := cursorColour isNil ifTrue: [self foregroundColourAt: (lineState fgRuns at: cc)] ifFalse: [cursorColour]].		 owner hasFocus			ifTrue: [aCanvas				drawString: lineState string				from: cc to: cc				in: (bounds insetOriginBy: (pitch * (cc - 1) @ 0) cornerBy: 0@0)				font: font				color: bg				background: fg]			ifFalse: [aCanvas				frameRectangle: (bounds origin + (pitch * (cc - 1) @ 0) extent: pitch @ font height)				width: 1				color: fg]].! !!SimpleTextMorph methodsFor: 'drawing'!drawLineOn: aCanvas	| port tmp |	self runsDo: [:l :r :fg0 :bg0 :em | | fg bg |		port := bounds left + (l - 1 * pitch) @ bounds top corner: bounds left + (lineState stringSize * pitch) @ bounds bottom.		(em bitAnd: 64) == 64 ifTrue: [fg := bg0. bg := fg0] ifFalse: [fg := fg0. bg := bg0].		aCanvas			"fillRectangle: port fillStyle: bg;"			drawString: lineState string			from: l to: r in: port			font: font			color: fg			background: bg.		(em bitAnd: 1) == 1 ifTrue:	"bold"			[aCanvas drawString: lineState string from: l to: r at: port topLeft + (1@0) font: font color: fg].		(em bitAnd: 8) == 8 ifTrue:	"underline"			[aCanvas line: port bottomLeft - (0@1) to: port bottomRight - (1@1) width: 1 color: fg]].! !!SimpleTextMorph methodsFor: 'drawing'!drawOn: aCanvas	super drawOn: aCanvas.	self		drawLineOn: aCanvas;		drawCursorOn: aCanvas! !!SimpleTextMorph methodsFor: 'drawing'!fitContents	"If the receiver has changed width then update my geometry."	self extent: lineState stringSize * pitch @ font height! !!SimpleTextMorph methodsFor: 'selection'!selectionColumnAt: screenPosition	"Answer the column of the character at the given screen screenPosition.  If the screenPosition is within our bounds vertically but outside to the left or right then answer the first or last + 1 column respectively.  If the screenPosition is beyond lastCol then answer our width + 1."	| x y col |	x := screenPosition x.	y := screenPosition y.	(self bounds top <= y and: [self bounds bottom >= y]) ifFalse: [^nil].	x <= self bounds left ifTrue: [^1].	x >= self bounds right ifTrue: [^lineState stringSize + 1].	col := screenPosition x - self bounds left // pitch + 1 min: lineState stringSize max: 1.	col > (lineState lastColumn + 1) ifTrue: [^lineState stringSize + 1].	^col! !!SimpleTextMorph methodsFor: 'private'!backgroundColourAt: i	^bgMap at: i + 1! !!SimpleTextMorph methodsFor: 'private'!defaultFont	^((TextStyle named: DefaultStyle) isNil		ifTrue: [TextStyle named: #Atlanta]		ifFalse: [TextStyle named: DefaultStyle]) defaultFont! !!SimpleTextMorph methodsFor: 'private'!flush	lineState changed		ifTrue:			[lineState changed: false.			 self changed]! !!SimpleTextMorph methodsFor: 'private'!foregroundColourAt: i	^fgMap at: i + 1! !!SimpleTextMorph methodsFor: 'private'!reversePolarity	"It's almost daybreak and my method names are getting silly."	fgMap swap: 1 with: fgMap size.	bgMap swap: 1 with: bgMap size.! !!SimpleTextMorph methodsFor: 'private'!runsDo: aBlock	| fg fgr bg bgr em emr left stringSize |	stringSize := lineState stringSize.	bgr := lineState bgRuns.  fgr := lineState fgRuns.  emr := lineState emRuns.	bg := bgr at: 1.  fg := fgr at: 1.  em := emr at: 1.	left := 1.	2 to: stringSize do: [:i |		(fg ~~ (fgr at: i) or: [bg ~~ (bgr at: i) or: [em ~~ (emr at: i)]])			ifTrue:				[aBlock					value: left value: i - 1					value: (self foregroundColourAt: fg)					value: (self backgroundColourAt: bg)					value: em.				 bg := bgr at: i.  fg := fgr at: i.  em := emr at: i.				 left := i]].	aBlock		value: left value: stringSize		value: (self foregroundColourAt: fg)		value: (self backgroundColourAt: bg)		value: em! !!SimpleTextMorph class methodsFor: 'class initialisation'!initialize	"SimpleTextMorph initialize"	"ISO 6429 colour indices"	Foreground := #(black red green yellow blue magenta cyan white) collect: [:c | Color perform: c].	Background := #(black red green yellow blue magenta cyan white) collect: [:c | Color perform: c].	"Alternate colour scheme for Emacs: reversed white/black on dark slate grey"	(Foreground2 := Foreground shallowCopy) at: 1 put: (Color white); at: 8 put: (Color black).	(Background2 := Background shallowCopy) at: 1 put: (Color white); at: 8 put: (Color fromString: '#314D52').	false ifTrue: [	2 to: 7 do: [:i |		Foreground2 at: i put: ((Foreground2 at: i) adjustSaturation: -0.35 brightness: 0.5).		Background2 at: i put: ((Background2 at: i) adjustSaturation: -0.35 brightness: 0.5)].	] ifFalse: [	(Foreground2 := Foreground collect: [:c | c adjustSaturation: -0.35 brightness: 0.5])		at: 1 put: (Color white "fromString: '#f7dfb5'"); at: 8 put: (Color black).	(Background2 := Background collect: [:c | c adjustSaturation: -0.35 brightness: 0.5])		at: 1 put: (Color white); at: 8 put: (Color fromString: '#314D52').	].	"Offer to create the standard X11 'fixed' font with VT100 graphics and line-drawing characters"	(TextStyle named: #Fixed) isNil ifTrue: [self initializeFonts].	self defaultFont: #Fixed.! !!SimpleTextMorph class methodsFor: 'class initialisation'!initializeFonts	"SimpleTextMorph initializeFonts"	| file |	(self confirm: 'The default fixed-width font is abominable.Do you want me to create a better fixed-width font for you?')		ifFalse: [^self].	file := FileStream newFileNamed: 'SimpleTextMorph-fixed.bdf'.	file nextPutAll: self fixedFontDefinition; close.	TextConstants at: #Fixed put: (TextStyle fontArray:		{StrikeFont newFromBDFFile: 'SimpleTextMorph-fixed.bdf' name: 'Fixed13'}).	FileDirectory default deleteFileNamed: 'SimpleTextMorph-fixed.bdf'.	self inform: 'Font created as TextStyle named: #Fixed'."---	(self confirm: 'The previous font is a little small on some high-resolution displays.Would you also like me to create a larger fixed-width font for you?')		ifFalse: [^self].	file := FileStream newFileNamed: 'SimpleTextMorph-lucida.bdf'.	file nextPutAll: self fixedFontDefinition2; close.	TextConstants at: #LucidaConsole put: (TextStyle fontArray:		{StrikeFont newFromBDFFile: 'SimpleTextMorph-lucida.bdf' name: 'LucidaConsole12'}).	FileDirectory default deleteFileNamed: 'SimpleTextMorph-lucida.bdf'.	self inform: 'Font created as TextStyle named: #LucidaConsole'.---"! !!SimpleTextMorph class methodsFor: 'instance creation'!contents: lineState	^super new contents: lineState! !!SimpleTextMorph class methodsFor: 'instance creation'!new	^super new initialize! !!SimpleTextMorph class methodsFor: 'accessing'!defaultFont	^DefaultStyle! !!SimpleTextMorph class methodsFor: 'accessing'!defaultFont: fontName	DefaultStyle := fontName! !!SimpleTextMorph class methodsFor: 'examples'!example	"SimpleTextMorph example"	| m |	(m := SimpleTextMorph new) lineState string: (String streamContents: [:s |			0 to: 79 do: [:i | s nextPut: (Character value: i \\ 26 + $a asciiValue)]]).	1 to: 80 do: [:i |		m lineState foregroundAt: i put: i \\ 8.		m lineState backgroundAt: i put: i - 1// 10].	m openInWorld; fitContents; flush.	^m! !!SimpleTextMorph class methodsFor: 'examples'!example2	"One way (the wrong way) to implement blinking cursors."	"SimpleTextMorph example2"	| m |	(m := SimpleTextMorph new) lineState string: ('Hello, world.' padded: #right to: 80 with: $ ).	m openInWorld; fitContents.	[10 timesRepeat:		[m lineState backgroundAt: 14 put: 0.  m flush.		 (Delay forMilliseconds: 600) wait.		 m lineState backgroundAt: 14 put: 7; changed.  m flush.		 (Delay forMilliseconds: 300) wait].	 m abandon] forkAt: Processor highIOPriority.	^m! !!SimpleTextMorph class methodsFor: 'examples'!example3	"SimpleTextMorph example3"	| m |	(m := SimpleTextMorph new) lineState string: ('Hello, world.' padded: #right to: 80 with: $ ).	m openInWorld; fitContents.	m cursorColour: Preferences textHighlightColor.	m lineState cursorCol: 14.	^m! !!SimpleTextMorph class methodsFor: 'private'!fixedFontDefinition	"This is -misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso8859-1 (aka 'fixed')"	^'STARTFONT 2.1COMMENT $XConsortium: 6x13.bdf,v 1.13 92/04/02 14:24:50 gildea Exp $COMMENT ""COMMENT Characters above 127 designed and made byCOMMENT Thomas Bagli (pyramid!!pcsbst!!tom@uunet.UU.NET)COMMENT PCS Computer Systeme, West GermanyCOMMENT ""FONT -Misc-Fixed-Medium-R-SemiCondensed--13-120-75-75-C-60-ISO8859-1SIZE 13 78 78FONTBOUNDINGBOX 6 13 0 -2STARTPROPERTIES 19FONTNAME_REGISTRY ""FOUNDRY "Misc"FAMILY_NAME "Fixed"WEIGHT_NAME "Medium"SLANT "R"SETWIDTH_NAME "SemiCondensed"ADD_STYLE_NAME ""PIXEL_SIZE 13POINT_SIZE 120RESOLUTION_X 75RESOLUTION_Y 75SPACING "C"AVERAGE_WIDTH 60CHARSET_REGISTRY "ISO8859"CHARSET_ENCODING "1"DEFAULT_CHAR 0FONT_DESCENT 2FONT_ASCENT 11COPYRIGHT "Public domain font.  Share and enjoy."ENDPROPERTIESCHARS 224STARTCHAR ascii000ENCODING 0SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00787878787878787878787800ENDCHARSTARTCHAR ascii001ENCODING 1SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000002070f87020000000ENDCHARSTARTCHAR ascii002ENCODING 2SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0054a854a854a854a854a854a8ENDCHARSTARTCHAR ascii003ENCODING 3SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000a0a0e0a0a070202020ENDCHARSTARTCHAR ascii004ENCODING 4SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000e080c080f040604040ENDCHARSTARTCHAR ascii005ENCODING 5SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000708080707048705048ENDCHARSTARTCHAR ascii006ENCODING 6SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000808080e07040604040ENDCHARSTARTCHAR ascii007ENCODING 7SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00006090906000000000000000ENDCHARSTARTCHAR ascii010ENCODING 8SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000002020f8202000f80000ENDCHARSTARTCHAR ascii011ENCODING 9SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000088c8a8988840404078ENDCHARSTARTCHAR ascii012ENCODING 10SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000008888502000f8202020ENDCHARSTARTCHAR ascii013ENCODING 11SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP20202020202020e00000000000ENDCHARSTARTCHAR ascii014ENCODING 12SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000e02020202020ENDCHARSTARTCHAR ascii015ENCODING 13SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000000003c2020202020ENDCHARSTARTCHAR ascii016ENCODING 14SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP202020202020203c0000000000ENDCHARSTARTCHAR ascii017ENCODING 15SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP20202020202020fc2020202020ENDCHARSTARTCHAR ascii020ENCODING 16SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000fc000000000000000000ENDCHARSTARTCHAR ascii021ENCODING 17SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000fc00000000000000ENDCHARSTARTCHAR ascii022ENCODING 18SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000fc0000000000ENDCHARSTARTCHAR ascii023ENCODING 19SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000000000000fc000000ENDCHARSTARTCHAR ascii024ENCODING 20SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000000000000000fc00ENDCHARSTARTCHAR ascii025ENCODING 21SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP202020202020203c2020202020ENDCHARSTARTCHAR ascii026ENCODING 22SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP20202020202020e02020202020ENDCHARSTARTCHAR ascii027ENCODING 23SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP20202020202020fc0000000000ENDCHARSTARTCHAR ascii030ENCODING 24SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000fc2020202020ENDCHARSTARTCHAR ascii031ENCODING 25SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP20202020202020202020202020ENDCHARSTARTCHAR ascii032ENCODING 26SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000008102040201008f80000ENDCHARSTARTCHAR ascii033ENCODING 27SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000080402010204080f80000ENDCHARSTARTCHAR ascii034ENCODING 28SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000f850505050900000ENDCHARSTARTCHAR ascii035ENCODING 29SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000008f820f880000000ENDCHARSTARTCHAR ascii036ENCODING 30SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000030484040e0404048b00000ENDCHARSTARTCHAR ascii037ENCODING 31SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000002000000000ENDCHARSTARTCHAR spaceENCODING 32SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000000000000ENDCHARSTARTCHAR exclamENCODING 33SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002020202020202000200000ENDCHARSTARTCHAR quotedblENCODING 34SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00005050500000000000000000ENDCHARSTARTCHAR numbersignENCODING 35SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000005050f850f85050000000ENDCHARSTARTCHAR dollarENCODING 36SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002078a0a0702828f0200000ENDCHARSTARTCHAR percentENCODING 37SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000048a85010204050a8900000ENDCHARSTARTCHAR ampersandENCODING 38SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000040a0a040a0989068000000ENDCHARSTARTCHAR quoterightENCODING 39SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00003020400000000000000000ENDCHARSTARTCHAR parenleftENCODING 40SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00001020204040402020100000ENDCHARSTARTCHAR parenrightENCODING 41SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00004020201010102020400000ENDCHARSTARTCHAR asteriskENCODING 42SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000020a8f870f8a820000000ENDCHARSTARTCHAR plusENCODING 43SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000002020f8202000000000ENDCHARSTARTCHAR commaENCODING 44SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000030204000ENDCHARSTARTCHAR hyphenENCODING 45SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000000f8000000000000ENDCHARSTARTCHAR periodENCODING 46SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000020702000ENDCHARSTARTCHAR slashENCODING 47SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000808101020404080800000ENDCHARSTARTCHAR zeroENCODING 48SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002050888888888850200000ENDCHARSTARTCHAR oneENCODING 49SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002060a02020202020f80000ENDCHARSTARTCHAR twoENCODING 50SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088880810204080f80000ENDCHARSTARTCHAR threeENCODING 51SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f808102070080888700000ENDCHARSTARTCHAR fourENCODING 52SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000101030505090f810100000ENDCHARSTARTCHAR fiveENCODING 53SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f88080b0c8080888700000ENDCHARSTARTCHAR sixENCODING 54SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000070888080f0888888700000ENDCHARSTARTCHAR sevenENCODING 55SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f808101020204040400000ENDCHARSTARTCHAR eightENCODING 56SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088888870888888700000ENDCHARSTARTCHAR nineENCODING 57SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088888878080888700000ENDCHARSTARTCHAR colonENCODING 58SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000207020000020702000ENDCHARSTARTCHAR semicolonENCODING 59SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000207020000030204000ENDCHARSTARTCHAR lessENCODING 60SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000810204080402010080000ENDCHARSTARTCHAR equalENCODING 61SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000f80000f800000000ENDCHARSTARTCHAR greaterENCODING 62SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008040201008102040800000ENDCHARSTARTCHAR questionENCODING 63SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088880810202000200000ENDCHARSTARTCHAR atENCODING 64SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000070888898a8a8b080780000ENDCHARSTARTCHAR AENCODING 65SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002050888888f88888880000ENDCHARSTARTCHAR BENCODING 66SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f048484870484848f00000ENDCHARSTARTCHAR CENCODING 67SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088808080808088700000ENDCHARSTARTCHAR DENCODING 68SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f048484848484848f00000ENDCHARSTARTCHAR EENCODING 69SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f8808080f0808080f80000ENDCHARSTARTCHAR FENCODING 70SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f8808080f0808080800000ENDCHARSTARTCHAR GENCODING 71SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088808080988888700000ENDCHARSTARTCHAR HENCODING 72SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000088888888f8888888880000ENDCHARSTARTCHAR IENCODING 73SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007020202020202020700000ENDCHARSTARTCHAR JENCODING 74SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00003810101010101090600000ENDCHARSTARTCHAR KENCODING 75SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000888890a0c0a09088880000ENDCHARSTARTCHAR LENCODING 76SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008080808080808080f80000ENDCHARSTARTCHAR MENCODING 77SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008888d8a8a8888888880000ENDCHARSTARTCHAR NENCODING 78SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000088c8c8a8a8989888880000ENDCHARSTARTCHAR OENCODING 79SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088888888888888700000ENDCHARSTARTCHAR PENCODING 80SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f0888888f0808080800000ENDCHARSTARTCHAR QENCODING 81SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000070888888888888a8700800ENDCHARSTARTCHAR RENCODING 82SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f0888888f0a09088880000ENDCHARSTARTCHAR SENCODING 83SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088808070080888700000ENDCHARSTARTCHAR TENCODING 84SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f820202020202020200000ENDCHARSTARTCHAR UENCODING 85SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008888888888888888700000ENDCHARSTARTCHAR VENCODING 86SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008888888850505020200000ENDCHARSTARTCHAR WENCODING 87SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000088888888a8a8a8d8880000ENDCHARSTARTCHAR XENCODING 88SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008888505020505088880000ENDCHARSTARTCHAR YENCODING 89SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008888505020202020200000ENDCHARSTARTCHAR ZENCODING 90SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f808101020404080f80000ENDCHARSTARTCHAR braketleftENCODING 91SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007040404040404040700000ENDCHARSTARTCHAR backslashENCODING 92SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00008080404020101008080000ENDCHARSTARTCHAR bracketrightENCODING 93SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007010101010101010700000ENDCHARSTARTCHAR asciicircumENCODING 94SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002050880000000000000000ENDCHARSTARTCHAR underscoreENCODING 95SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000000000000000f800ENDCHARSTARTCHAR quoteleftENCODING 96SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00003010080000000000000000ENDCHARSTARTCHAR aENCODING 97SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007008788888780000ENDCHARSTARTCHAR bENCODING 98SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000808080f088888888f00000ENDCHARSTARTCHAR cENCODING 99SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088808088700000ENDCHARSTARTCHAR dENCODING 100SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000808087888888888780000ENDCHARSTARTCHAR eENCODING 101SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088f88088700000ENDCHARSTARTCHAR fENCODING 102SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000030484040f0404040400000ENDCHARSTARTCHAR gENCODING 103SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088888878088870ENDCHARSTARTCHAR hENCODING 104SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000808080b0c8888888880000ENDCHARSTARTCHAR iENCODING 105SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000020006020202020700000ENDCHARSTARTCHAR jENCODING 106SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000010003010101010909060ENDCHARSTARTCHAR kENCODING 107SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000080808090a0c0a090880000ENDCHARSTARTCHAR lENCODING 108SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00006020202020202020700000ENDCHARSTARTCHAR mENCODING 109SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000d0a8a8a8a8880000ENDCHARSTARTCHAR nENCODING 110SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000b0c8888888880000ENDCHARSTARTCHAR oENCODING 111SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088888888700000ENDCHARSTARTCHAR pENCODING 112SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000f0888888f0808080ENDCHARSTARTCHAR qENCODING 113SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007888888878080808ENDCHARSTARTCHAR rENCODING 114SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000b0c8808080800000ENDCHARSTARTCHAR sENCODING 115SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088601088700000ENDCHARSTARTCHAR tENCODING 116SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000004040f040404048300000ENDCHARSTARTCHAR uENCODING 117SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008888888898680000ENDCHARSTARTCHAR vENCODING 118SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008888885050200000ENDCHARSTARTCHAR wENCODING 119SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008888a8a8a8500000ENDCHARSTARTCHAR xENCODING 120SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008850202050880000ENDCHARSTARTCHAR yENCODING 121SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008888889868088870ENDCHARSTARTCHAR zENCODING 122SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000f810204080f80000ENDCHARSTARTCHAR braceleftENCODING 123SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000018202020c0202020180000ENDCHARSTARTCHAR barENCODING 124SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002020202020202020200000ENDCHARSTARTCHAR bracerightENCODING 125SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000c020202018202020c00000ENDCHARSTARTCHAR asciitildeENCODING 126SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000048a8900000000000000000ENDCHARSTARTCHAR ascii177ENCODING 127SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000000000000ENDCHARSTARTCHAR 00a0ENCODING 160SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000000000000ENDCHARSTARTCHAR 00a1ENCODING 161SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002000202020202020200000ENDCHARSTARTCHAR 00a2ENCODING 162SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002070a8a0a0a87020000000ENDCHARSTARTCHAR 00a3ENCODING 163SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000030484040e0404048b00000ENDCHARSTARTCHAR 00a4ENCODING 164SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000887050507088000000ENDCHARSTARTCHAR 00a5ENCODING 165SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000088885050f820f820200000ENDCHARSTARTCHAR 00a6ENCODING 166SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002020202000202020200000ENDCHARSTARTCHAR 00a7ENCODING 167SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304840304848300848300000ENDCHARSTARTCHAR 00a8ENCODING 168SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000d800000000000000000000ENDCHARSTARTCHAR 00a9ENCODING 169SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP007088a8d8c8d8a88870000000ENDCHARSTARTCHAR 00aaENCODING 170SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000700878887800f800000000ENDCHARSTARTCHAR 00abENCODING 171SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000002850a0a05028000000ENDCHARSTARTCHAR 00acENCODING 172SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000000f8080800000000ENDCHARSTARTCHAR 00adENCODING 173SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000000000f8000000000000ENDCHARSTARTCHAR 00aeENCODING 174SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP007088e8d8d8e8d88870000000ENDCHARSTARTCHAR 00afENCODING 175SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f800000000000000000000ENDCHARSTARTCHAR 00b0ENCODING 176SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00003048483000000000000000ENDCHARSTARTCHAR 00b1ENCODING 177SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000002020f8202000f8000000ENDCHARSTARTCHAR 00b2ENCODING 178SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040a02040e000000000000000ENDCHARSTARTCHAR 00b3ENCODING 179SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040a04020c000000000000000ENDCHARSTARTCHAR 00b4ENCODING 180SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000000000000000000000ENDCHARSTARTCHAR 00b5ENCODING 181SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008888888898e88000ENDCHARSTARTCHAR 00b6ENCODING 182SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000078e8e8e8e8682828280000ENDCHARSTARTCHAR 00b7ENCODING 183SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000030000000000000ENDCHARSTARTCHAR 00b8ENCODING 184SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000000000000000001020ENDCHARSTARTCHAR 00b9ENCODING 185SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040c04040e000000000000000ENDCHARSTARTCHAR 00baENCODING 186SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00003048483000780000000000ENDCHARSTARTCHAR 00bbENCODING 187SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000a050282850a0000000ENDCHARSTARTCHAR 00bcENCODING 188SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040c04040e008182838080000ENDCHARSTARTCHAR 00bdENCODING 189SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040c04040e010280810380000ENDCHARSTARTCHAR 00beENCODING 190SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0040a04020a048182838080000ENDCHARSTARTCHAR 00bfENCODING 191SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00002000202040808888700000ENDCHARSTARTCHAR 00c0ENCODING 192SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0020100020508888f888880000ENDCHARSTARTCHAR 00c1ENCODING 193SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0010200020508888f888880000ENDCHARSTARTCHAR 00c2ENCODING 194SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0030480020508888f888880000ENDCHARSTARTCHAR 00c3ENCODING 195SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0028500020508888f888880000ENDCHARSTARTCHAR 00c4ENCODING 196SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0050500020508888f888880000ENDCHARSTARTCHAR 00c5ENCODING 197SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0020502020508888f888880000ENDCHARSTARTCHAR 00c6ENCODING 198SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000058a0a0a0b0e0a0a0b80000ENDCHARSTARTCHAR 00c7ENCODING 199SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00007088808080808088702040ENDCHARSTARTCHAR 00c8ENCODING 200SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000f88080f08080f80000ENDCHARSTARTCHAR 00c9ENCODING 201SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000f88080f08080f80000ENDCHARSTARTCHAR 00caENCODING 202SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00609000f88080f08080f80000ENDCHARSTARTCHAR 00cbENCODING 203SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000f88080f08080f80000ENDCHARSTARTCHAR 00ccENCODING 204SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000f82020202020f80000ENDCHARSTARTCHAR 00cdENCODING 205SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000f82020202020f80000ENDCHARSTARTCHAR 00ceENCODING 206SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800f82020202020f80000ENDCHARSTARTCHAR 00cfENCODING 207SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000f82020202020f80000ENDCHARSTARTCHAR 00d0ENCODING 208SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000f0484848e8484848f00000ENDCHARSTARTCHAR 00d1ENCODING 209SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP002850008888c8a89888880000ENDCHARSTARTCHAR 00d2ENCODING 210SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000708888888888700000ENDCHARSTARTCHAR 00d3ENCODING 211SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000708888888888700000ENDCHARSTARTCHAR 00d4ENCODING 212SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800708888888888700000ENDCHARSTARTCHAR 00d5ENCODING 213SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00285000708888888888700000ENDCHARSTARTCHAR 00d6ENCODING 214SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000708888888888700000ENDCHARSTARTCHAR 00d7ENCODING 215SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000008850205088000000ENDCHARSTARTCHAR 00d8ENCODING 216SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0008709898a8a8a8c8c8708000ENDCHARSTARTCHAR 00d9ENCODING 217SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000888888888888700000ENDCHARSTARTCHAR 00daENCODING 218SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000888888888888700000ENDCHARSTARTCHAR 00dbENCODING 219SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800888888888888700000ENDCHARSTARTCHAR 00dcENCODING 220SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000888888888888700000ENDCHARSTARTCHAR 00ddENCODING 221SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000888850202020200000ENDCHARSTARTCHAR 00deENCODING 222SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000080f0888888f08080800000ENDCHARSTARTCHAR 00dfENCODING 223SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000708888f08888c8b08000ENDCHARSTARTCHAR 00e0ENCODING 224SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000007008788898680000ENDCHARSTARTCHAR 00e1ENCODING 225SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000007008788898680000ENDCHARSTARTCHAR 00e2ENCODING 226SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800007008788898680000ENDCHARSTARTCHAR 00e3ENCODING 227SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00285000007008788898680000ENDCHARSTARTCHAR 00e4ENCODING 228SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000007008788898680000ENDCHARSTARTCHAR 00e5ENCODING 229SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304830007008788898680000ENDCHARSTARTCHAR 00e6ENCODING 230SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000000000702870a0a8500000ENDCHARSTARTCHAR 00e7ENCODING 231SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000007088808088702040ENDCHARSTARTCHAR 00e8ENCODING 232SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000007088f88088700000ENDCHARSTARTCHAR 00e9ENCODING 233SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000007088f88088700000ENDCHARSTARTCHAR 00eaENCODING 234SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800007088f88088700000ENDCHARSTARTCHAR 00ebENCODING 235SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000007088f88088700000ENDCHARSTARTCHAR 00ecENCODING 236SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000006020202020f80000ENDCHARSTARTCHAR 00edENCODING 237SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000006020202020f80000ENDCHARSTARTCHAR 00eeENCODING 238SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800006020202020f80000ENDCHARSTARTCHAR 00efENCODING 239SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000006020202020f80000ENDCHARSTARTCHAR 00f0ENCODING 240SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00502060107088888888700000ENDCHARSTARTCHAR 00f1ENCODING 241SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0028500000b0c8888888880000ENDCHARSTARTCHAR 00f2ENCODING 242SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000007088888888700000ENDCHARSTARTCHAR 00f3ENCODING 243SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000007088888888700000ENDCHARSTARTCHAR 00f4ENCODING 244SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800007088888888700000ENDCHARSTARTCHAR 00f5ENCODING 245SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00285000007088888888700000ENDCHARSTARTCHAR 00f6ENCODING 246SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000007088888888700000ENDCHARSTARTCHAR 00f7ENCODING 247SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP000000202000f8002020000000ENDCHARSTARTCHAR 00f8ENCODING 248SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00000000087098a8a8c8708000ENDCHARSTARTCHAR 00f9ENCODING 249SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00201000008888888888780000ENDCHARSTARTCHAR 00faENCODING 250SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000008888888888780000ENDCHARSTARTCHAR 00fbENCODING 251SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00304800008888888888780000ENDCHARSTARTCHAR 00fcENCODING 252SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000008888888888780000ENDCHARSTARTCHAR 00fdENCODING 253SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00102000008888889868088870ENDCHARSTARTCHAR 00feENCODING 254SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP0000008080b0c88888c8b08080ENDCHARSTARTCHAR 00ffENCODING 255SWIDTH 461 0DWIDTH 6 0BBX 6 13 0 -2BITMAP00505000008888889868088870ENDCHARENDFONT'! !!SimpleTextState methodsFor: 'initialize-release'!initialize	"Initialize the receiver with empty contents."	self initialize: (String new: 0)! !!SimpleTextState methodsFor: 'initialize-release'!initialize: aString	"Initialize the receiver with the given contents."	string := aString.	stringSize := string size.	fgRuns := Array new: stringSize withAll: 0.	bgRuns := Array new: stringSize withAll: 7.	emRuns := Array new: stringSize withAll: 0.	cursorCol := 0.	changed := true.	"want initial display"	selection := nil.	lastCol := 0.! !!SimpleTextState methodsFor: 'accessing'!at: index insert: char fg: fg bg: bg em: em	stringSize - 1 to: index by: -1 do: [:i |		string at: i + 1 put: (string at: i).		fgRuns at: i + 1 put: (fgRuns at: i).		bgRuns at: i + 1 put: (bgRuns at: i).		emRuns at: i + 1 put: (emRuns at: i)].	string at: index put: char.	self foregroundAt: index put: fg.	self backgroundAt: index put: bg.	lastCol := lastCol + 1 min: stringSize max: index.	changed := true! !!SimpleTextState methodsFor: 'accessing'!at: index put: char fg: fg bg: bg em: em	((string at: index) ~~ char			or: [(fgRuns at: index) ~~ fg			or: [(bgRuns at: index) ~~ bg			or: [(emRuns at: index) ~~em]]])		ifTrue:			[string at: index put: char.			 self foregroundAt: index put: fg.			 self backgroundAt: index put: bg.			 emRuns at: index put: em.			 lastCol := lastCol max: index.			 changed := true]! !!SimpleTextState methodsFor: 'accessing'!atAllPut: char fg: fg bg: bg em: em	"Update the receiver's string, colour and emphasis."	string from: 1 to: stringSize put: char.	fgRuns from: 1 to: stringSize put: fg.	bgRuns from: 1 to: stringSize put: bg.	emRuns from: 1 to: stringSize put: em.	lastCol := char == $  ifTrue: [1] ifFalse: [stringSize].	changed := true! !!SimpleTextState methodsFor: 'accessing'!background: bgIndex	1 to: stringSize do: [:i | self backgroundAt: i put: bgIndex]! !!SimpleTextState methodsFor: 'accessing'!backgroundAt: index put: c	(bgRuns at: index) ~~ c		ifTrue:			[bgRuns at: index put: c.			 changed := true]! !!SimpleTextState methodsFor: 'accessing'!basicAt: index put: char fg: fg bg: bg em: em	string at: index put: char.	self foregroundAt: index put: fg.	self backgroundAt: index put: bg.	emRuns at: index put: em.	lastCol := lastCol max: index.! !!SimpleTextState methodsFor: 'accessing'!bgRuns	^bgRuns! !!SimpleTextState methodsFor: 'accessing'!changed	^changed! !!SimpleTextState methodsFor: 'accessing'!changed: aBoolean	changed := aBoolean! !!SimpleTextState methodsFor: 'accessing'!cursorCol: x	cursorCol := x.	changed := true! !!SimpleTextState methodsFor: 'accessing'!cursorColumn	^cursorCol! !!SimpleTextState methodsFor: 'accessing'!deleteCharAt: index	"Delete the character at index, moving anything to the right of it left one column."	index to: stringSize - 1 do: [:i |		string at: i put: (string at: i + 1).		fgRuns at: i put: (fgRuns at: i + 1).		bgRuns at: i put: (bgRuns at: i + 1).		emRuns at: i put: (emRuns at: i + 1)].	string at: stringSize put: $ .	lastCol := lastCol - 1 max: 0.	changed := true! !!SimpleTextState methodsFor: 'accessing'!emRuns	^emRuns! !!SimpleTextState methodsFor: 'accessing'!fgRuns	^fgRuns! !!SimpleTextState methodsFor: 'accessing'!foreground: fbIndex	1 to: stringSize do: [:i | self foregroundAt: i put: fbIndex]! !!SimpleTextState methodsFor: 'accessing'!foregroundAt: index put: c	(fgRuns at: index) ~~ c		ifTrue:			[fgRuns at: index put: c.			 changed := true]! !!SimpleTextState methodsFor: 'accessing'!from: start to: stop put: char fg: fg bg: bg em: em	string from: start to: stop put: char.	fgRuns from: start to: stop put: fg.	bgRuns from: start to: stop put: bg.	emRuns from: start to: stop put: em.	changed := true! !!SimpleTextState methodsFor: 'accessing'!insertAt: index	stringSize - 1 to: index by: -1 do: [:i |		string at: i + 1 put: (string at: i).		fgRuns at: i + 1 put: (fgRuns at: i).		bgRuns at: i + 1 put: (bgRuns at: i).		emRuns at: i + 1 put: (emRuns at: i)].	lastCol := lastCol + 1 min: stringSize max: index.	changed := true! !!SimpleTextState methodsFor: 'accessing'!lastColumn	"Answer the rightmost column into which a character has been explicitly written."	^lastCol! !!SimpleTextState methodsFor: 'accessing'!lastColumn: col	"Somebody has deleted everythig from col onwards."	lastCol := col! !!SimpleTextState methodsFor: 'accessing'!selection	"Answer the selection range for this line."	^selection! !!SimpleTextState methodsFor: 'accessing'!setWidth: width	string size < width		ifTrue: [self string: (string forceTo: width paddingWith: $ )]		ifFalse: [string size > width			ifTrue: [self from: width + 1 to: string size  put: $  fg: 0 bg: 7 em: 0]].	stringSize := width.	self findLastColumn.! !!SimpleTextState methodsFor: 'accessing'!string	^string! !!SimpleTextState methodsFor: 'accessing'!string: aString	"Replace the receiver's string with aString.  If the receiver changes width then also modify the colours and emphasis retaining the overlapping portions and extending with default colours and emphasis, and then update the geometry.  Reset lastCol to be the index of the last non-whitespace in aString."	| oldSize newSize |	oldSize := string size.	newSize := aString size.	string := aString.	oldSize < newSize		ifTrue:			[fgRuns := fgRuns forceTo: newSize paddingWith: 0.			 bgRuns := bgRuns forceTo: newSize paddingWith: 7.			 emRuns := emRuns forceTo: newSize paddingWith: 0]		ifFalse:			[fgRuns from: newSize + 1 to: oldSize put: 0.			 bgRuns from: newSize + 1 to: oldSize put: 7.			 emRuns from: newSize + 1 to: oldSize put: 0].	cursorCol := cursorCol min: newSize.	stringSize := newSize.	self findLastColumn.	selection := nil! !!SimpleTextState methodsFor: 'accessing'!stringSize	^stringSize! !!SimpleTextState methodsFor: 'copying'!copy	^self deepCopy! !!SimpleTextState methodsFor: 'selection'!appendSelectionTo: aStream	selection isNil ifTrue: [^self].	"no selection"	selection first > lastCol ifTrue: [^aStream cr].	"only the end of line is selected"	selection first to: (selection last min: lastCol) do: [:i | aStream nextPut: (string at: i)].	"line contents are selected"	selection last > lastCol ifTrue: [aStream cr].	"end of line is included in selection"! !!SimpleTextState methodsFor: 'selection'!clearSelection	selection isNil ifTrue: [^self].	selection first to: selection last do: [:i | emRuns at: i put: ((emRuns at: i) bitXor: 64)].	selection := nil.	changed := true! !!SimpleTextState methodsFor: 'selection'!findFirstInClass: charClasses from: start	"Find the index of the leftmost character in the sequence of characters beginning at start that all belong to the same class in charClasses."	| charClass |	charClass := charClasses at: (string at: start) asciiValue + 1.	start - 1 to: 1 by: -1 do: [ :i | (charClasses at: (string at: i) asciiValue + 1) == charClass ifFalse: [^i + 1]].	^1.! !!SimpleTextState methodsFor: 'selection'!findLastInClass: charClasses from: start	"Find the index of the rightmost character in the sequence of characters beginning at start that all belong to the same class in charClasses."	| charClass |	charClass := charClasses at: (string at: start) asciiValue + 1.	start + 1 to: lastCol do: [ :i | (charClasses at: (string at: i) asciiValue + 1) == charClass ifFalse: [^i - 1]].	^lastCol.! !!SimpleTextState methodsFor: 'selection'!selectFrom: left to: right	selection isNil ifFalse: [self clearSelection].	selection := Array with: left with: right.	left to: right do: [:i | emRuns at: i put: ((emRuns at: i) bitXor: 64)].	changed := true! !!SimpleTextState methodsFor: 'selection'!selectionSpansColumn: index	"Answer whether index is inside the current selection range."	^selection notNil and: [index >= selection first and: [index <= selection last]]! !!SimpleTextState methodsFor: 'private'!findLastColumn	stringSize to: 1 by: -1 do: [ :i | (string at: i) == $  ifFalse: [^lastCol := i]].	^lastCol := 0.! !!SimpleTextState class methodsFor: 'instance creation'!new	^super new initialize! !!SimpleTextState class methodsFor: 'instance creation'!new: size	^self string: (String new: size withAll: $ )! !!SimpleTextState class methodsFor: 'instance creation'!string: aString	^super new initialize: aString! !!StatefulProtocol methodsFor: 'accessing'!client: anObject	client := anObject! !!StatefulProtocol methodsFor: 'accessing'!initialState: stateNode	currentState := stateNode.	self client: self! !!StatefulProtocol methodsFor: 'accessing'!state	^currentState! !!StatefulProtocol methodsFor: 'protocol'!upcall: anObject	currentState := (currentState transitionAt: anObject)		transitionFrom: currentState for: client with: anObject! !!StatefulProtocol class methodsFor: 'instance creation'!initialState: stateNode	^self new initialState: stateNode! !!StatefulProtocol class methodsFor: 'instance creation'!initialState: stateNode client: aClient	^self new initialState: stateNode; client: aClient! !!StatefulProtocolDescription methodsFor: 'initialize-release'!initialState: stateName	initialState := stateName! !!StatefulProtocolDescription methodsFor: 'initialize-release'!newState: rule	^self newState: rule key default: rule value! !!StatefulProtocolDescription methodsFor: 'initialize-release'!newState: stateName default: transition	^self at: stateName put: (ProtocolState name: stateName default: transition)! !!StatefulProtocolDescription methodsFor: 'printing'!printElementsOn: aStream	aStream nextPutAll: '(initial: ' , initialState printString , ' states:'.	self keysDo: [:key | aStream space.  key printOn: aStream].	aStream nextPut: $)! !!StatefulProtocolDescription methodsFor: 'compiling'!compile	"Compile my symbolic representation into a cyclic DAG and answer the root node"	| edge |	self valuesDo: [:state |		state defaultTransition: (self resolve: state default).		state keysDo: [:key |			edge := state at: key.			state transitionAt: key put: (self resolve: edge)]].		^self at: initialState! !!StatefulProtocolDescription methodsFor: 'compiling'!resolve: edge	| target action |	target := edge state.	action := edge action.	target := (self includesKey: target)		ifTrue: [self at: target]		ifFalse: [target isNil					ifTrue: [nil]					ifFalse: [self error: 'unknown target state ' , edge printString]].	^ProtocolStateTransition action: action state: target! !!StatefulProtocolDescription class methodsFor: 'instance creation'!initialState: stateName	^self new initialState: stateName! !!StatefulProtocolDescription class methodsFor: 'examples'!example	"A state machine that recognises occurrences of 'x' 'xy' and 'xy[digits...]z' in a stream of characters.  Note: this is used by StateMachineTester, so don't modify it.  See StateMachineTester class>>test for an example of use."	"StatefulProtocolDescription example"	| desc |	desc := self new.	(desc newState: #initial -> (#echo: -> #initial)) add: $x -> (nil -> #statex).	(desc newState: #statex -> (#echox: -> #initial)) add: $y -> (#initPrefix: -> #statexy).	(desc newState: #statexy -> (#echoxy: -> #initial))		add: $z -> (#echoxyz: -> #initial);		addAll: '0123456789' -> (#addPrefix: -> nil).	desc initialState: #initial.	^desc! !!StatefulProtocolDescription class methodsFor: 'examples'!example2	"StatefulProtocolDescription example2 explore"	^self example compile! !!StatefulProtocolDescription class methodsFor: 'examples'!example3		"Note: this example should pop up an error notifier during compilation"	"StatefulProtocolDescription example3 compile"	| desc |	desc := self new.	(desc newState: #initial -> (#echo: -> #initial)) add: $x -> (nil -> #statex).	(desc newState: #statex -> (#echox: -> #initial)) add: $y -> (nil -> #statexy).	(desc newState: #statexy -> (#echoxy: -> #initial)) add: $z -> (#echoxy: -> #statexyz).	desc initialState: #initial.	^desc! !!StatefulProtocolTester methodsFor: 'state transitions'!step: anObject	Transcript cr;		print: currentState name;		nextPutAll: ' step: ';		print: anObject;		nextPutAll: ' -> ';		endEntry.	self upcall: anObject.! !!StatefulProtocolTester methodsFor: 'actions'!addPrefix: anObject	prefix := prefix * 10 + anObject asInteger - 48! !!StatefulProtocolTester methodsFor: 'actions'!echo: anObject	Transcript show: anObject printString! !!StatefulProtocolTester methodsFor: 'actions'!echox: anObject	Transcript show: ' <saw X> ' , anObject printString! !!StatefulProtocolTester methodsFor: 'actions'!echoxy: anObject	Transcript show: ' <saw XY> ' , anObject printString! !!StatefulProtocolTester methodsFor: 'actions'!echoxyz: anObject	Transcript show: ' <saw XYZ prefix ' , prefix printString , ' > ' , anObject printString! !!StatefulProtocolTester methodsFor: 'actions'!initPrefix: anObject	prefix := 0! !!StatefulProtocolTester class methodsFor: 'examples'!test	"StatefulProtocolTester test"	| sm input |	sm := self initialState: (StatefulProtocolDescription example compile).	sm client: sm.	input := '1x2xx3xxx4y5xy6yy7xyx8xyy9xyz10zyx'.	Transcript cr; show: 'input is ' , input printString ; cr.	'1x2xx3xxx4y5xy6yy7xyx8xyy9xyz10xy42zzyx' do: [: c | sm step: c].	Transcript cr; show: ' final state is ' , sm state name printString; cr.! !!StatefulProtocolTester2 methodsFor: 'initialize-release'!reset	e := x := xy := xyz := 0! !!StatefulProtocolTester2 methodsFor: 'actions'!echo: anObject	e := e + 1! !!StatefulProtocolTester2 methodsFor: 'actions'!echox: anObject	x := x + 1! !!StatefulProtocolTester2 methodsFor: 'actions'!echoxy: anObject	xy := xy + 1! !!StatefulProtocolTester2 methodsFor: 'actions'!echoxyz: anObject	xyz := xyz + 1! !!StatefulProtocolTester2 methodsFor: 'printing'!printOn: aStream	aStream cr; nextPutAll: 'saw ',		e printString, ' unmatched characters, ',		x printString, ' x, ',		xy printString, ' xy, ',		xyz printString, ' xyz'.! !!StatefulProtocolTester2 class methodsFor: 'examples'!test	"A version of StatefulProtocolTester that measures throughput (in transitions per second)."	"StatefulProtocolTester2 test"	| sm input stream size time |	sm := self initialState: (StatefulProtocolDescription example compile).	sm client: sm.	input := '1x2xx3xxx4y5xy6yy7xyx8xyy9xyz10zyx'.	Transcript cr; show: 'input is ' , input printString ; cr.	stream := WriteStream on: input.	[stream contents size < 100000] whileTrue: [stream nextPutAll: input].	Transcript show: 'size is ' , (size := stream contents size) printString; cr.	input := ReadStream on: stream contents.	time := Time millisecondsToRun: [sm reset; upcallAll: input].	Transcript show: sm printString , ' in ' , time printString , 'ms'; cr.	Transcript show: (size / time * 1000) asInteger printString,		' state transitions per second'! !!StrikeFont methodsFor: 'testing'!isMonospaced	| widths |	widths := ((0 to: 255) collect: [ :n | self widthOf: (Character value: n) ]) asBag.	^(widths sortedElements reject: [ :a | a key == 0 ]) size < 3! !!StrikeFont class methodsFor: 'accessing'!monospacedFamilyNames	| retval |	retval := Set new.	(TextConstants select: [ :ea | ea isKindOf: TextStyle ]) do: [ :family |		retval addAll: ((family fonts select: [ :font | font isMonospaced ]) collect: [ :font | font familyName asSymbol ])	].	^retval! !!TeletypeMorph methodsFor: 'initialize-release'!initialize	^self initialize: 80@24! !!TeletypeMorph methodsFor: 'initialize-release'!initialize: size	super initialize.	borderWidth := 2.	color := Color white.	inset := 2.	trackingSelection := false.	useScrollbar := false.	self		initializeTeletype: size;		initializeContent;		initializeScrollbar;		extent: self preferredExtent! !!TeletypeMorph methodsFor: 'initialize-release'!initializeContent	| morphs |	morphs := OrderedCollection new.	rows timesRepeat: [morphs addLast: ((SimpleTextMorph contents: (lines addLast: self newLine)) cursorColour: cursorColour)].	self addAllMorphs: morphs.	topLine := 1.	bottomLine := rows.	x := y := 1.	self banner do: [:c | self upcall: c asInteger]! !!TeletypeMorph methodsFor: 'initialize-release'!initializeScrollbar	scrollFlop := (Preferences valueOfFlag: #inboardScrollbars) not.	scrollRight := (Preferences valueOfFlag: #scrollBarsOnRight).	useScrollbar & scrollFlop not ifTrue: [self showScrollbar]! !!TeletypeMorph methodsFor: 'initialize-release'!initializeTabs	tabs := Array new: cols withAll: false.	1 to: cols by: 8 do: [:i | tabs at: i put: true]! !!TeletypeMorph methodsFor: 'initialize-release'!initializeTeletype: size	"Initialize the default behaviour: dumb terminal with local echo and default xterm VT options."	font := (TextConstants at: (SimpleTextMorph defaultFont)) defaultFont.	pitch := font widthOf: $m.	skip := font height.	cols := size x.  rows := size y.	lines := OrderedCollection new.	savedLines := 0.	savedLineLimit := SavedLineLimit.	displayStart := 0.	x := 1.  y := 1.	fg := 0.  bg := 7.  em := 0.	rv := false.  ec := false.	session := nil.	self initializeTabs.	self initializeTerminalModes.	"Default low protocol: local echo"	down := (ProtocolAdaptor new localEcho up: self).	showCursor := true.	running := false.	autoFlush := 0.	steps := 0.	altScreenActive := false.	altScreenColours := false.	hasFocus := true.	selectionStart := selectionEnd := nil.	selectionActive := false.	selection := ''.	mouseControlsSelection := MouseControlsSelection.	keyboardControlsSelection := KeyboardControlsSelection.	scrollOnInput := false.	scrollOnOutput := true.	allow132 := true.	characterClasses := CharClass copy.	cursorColour := nil.! !!TeletypeMorph methodsFor: 'initialize-release'!initializeTerminalModes	"Initialize the default behaviour: dumb terminal with local echo and standard xterm VT option settings."	autoWrap := true.	reverseWrap := autoLinefeed := autoCR := relativeOrigin := insertMode := false.	showCursor := true.	smoothScroll := false.	metaSendsEscape := true.	deleteIsDel := false.	altScreenSwitch := true.	reverseVideo := false.! !!TeletypeMorph methodsFor: 'initialize-release'!openInWorld	self currentHand newKeyboardFocus: nil.	super openInWorld.! !!TeletypeMorph methodsFor: 'initialize-release'!session: aSession	session := aSession! !!TeletypeMorph methodsFor: 'initialize-release'!setWindow: aWindow	systemWindow := aWindow! !!TeletypeMorph methodsFor: 'accessing'!activeColumn	^x! !!TeletypeMorph methodsFor: 'accessing'!activeColumn: c	x := c min: cols.	self showCursor.! !!TeletypeMorph methodsFor: 'accessing'!bufferState	"Answer an opaque representation of the current state of the buffer contents."	| theLines state |	self clearSelection.	theLines := OrderedCollection new.	self linesDo: [:line | theLines addLast: line copy].	state := Array with: theLines asArray with: rv with: altScreenActive with: x with: y with: cursorColour.	altScreenActive := true.	^state! !!TeletypeMorph methodsFor: 'accessing'!bufferState: state	"Restore the buffer contents to a previously saved state."	| theLines nRows |	altScreenSwitch ifFalse: [^self].	self clearSelection; clearCursor.	theLines := state at: 1.	rv := state at: 2.	altScreenActive := state at: 3.	"Ensure we have sufficient lines in the display."	[lines size < theLines size]		whileTrue:			[lines addLast: (SimpleTextState new: cols).			 self addMorphBack: (SimpleTextMorph contents: lines last)].	nRows := rows min: theLines size.	1 to: nRows do: [ :i |		(submorphs at: i)			lineState:			(self displayLineAt: i put:				((theLines at: i) setWidth: cols))].	nRows + 1 to: rows do: [:i | self clearLine: i from: 1 to: cols].	submorphs from: 1 to: rows do: [ :m | m rv: (rv xor: reverseVideo)].	x := (state at: 4) min: cols max: 1.	y := (state at: 5) min: rows max: 1.	cursorColour := state at: 6.	self showCursor; linesChanged; changed! !!TeletypeMorph methodsFor: 'accessing'!characterClass	^CharClass! !!TeletypeMorph methodsFor: 'accessing'!columns	^cols! !!TeletypeMorph methodsFor: 'accessing'!deleteIsDel	^deleteIsDel! !!TeletypeMorph methodsFor: 'accessing'!deleteIsDel: aBoolean	deleteIsDel := aBoolean! !!TeletypeMorph methodsFor: 'accessing'!graphicsState	"Answer an opaque representation of the current character attributes."	^Array with: fg with: bg with: em with: rv! !!TeletypeMorph methodsFor: 'accessing'!graphicsState: gs	"Restore the current character attributes.  (See #graphicsState.)"	fg := gs at: 1.	bg := gs at: 2.	em := gs at: 3! !!TeletypeMorph methodsFor: 'accessing'!hasFocus	^hasFocus! !!TeletypeMorph methodsFor: 'accessing'!metaSendsEscape	^metaSendsEscape! !!TeletypeMorph methodsFor: 'accessing'!pitch	^pitch! !!TeletypeMorph methodsFor: 'accessing'!rows	^rows! !!TeletypeMorph methodsFor: 'accessing'!rv: aBoolean	"Set the programmed reverse video flag.  Not to be confused with reverseVideo, which is a user preference that inverts the sense of the programmed change."	rv == aBoolean ifTrue: [^self].	rv := aBoolean..	submorphs from: 1 to: rows do: [ :m | m rv: (rv xor: reverseVideo)].	self changed! !!TeletypeMorph methodsFor: 'accessing'!skip	^skip! !!TeletypeMorph methodsFor: 'testing'!isCollapsed	^(systemWindow notNil) and: [systemWindow isCollapsed]! !!TeletypeMorph methodsFor: 'events'!handlesKeyboard: evt	"Answer whether we're interested in keyboard events."	^true "hasFocus or: [super handlesKeyboard: evt]"! !!TeletypeMorph methodsFor: 'events'!handlesMouseDown: evt	"Answer whether we're interested in mouse events."	^true "self isConnected and: [hasFocus or: [super handlesMouseDown: evt]]"! !!TeletypeMorph methodsFor: 'events'!handlesMouseOver: evt	^true "hasFocus or: [super handlesMouseOver: evt]"! !!TeletypeMorph methodsFor: 'events'!keyStroke: evt	"Receive a character from the keyboard."	| char |	scrollOnInput ifTrue: [self pageEnd].	char := evt keyCharacter asciiValue.	evt controlKeyPressed		ifFalse:			[char == 1 ifTrue: [^self pageHome].			 char == 4 ifTrue: [^self pageEnd].			 char == 11 ifTrue: [^self pageUp: rows // 2].			 char == 12 ifTrue: [^self pageDown: rows // 2]].	(keyboardControlsSelection and: [evt commandKeyPressed])		ifTrue:			[evt keyCharacter == $c ifTrue: [^self copySelection].			 evt keyCharacter == $v ifTrue: [^self sendSelection]].	(metaSendsEscape and: [evt commandKeyPressed])		ifTrue: [down downcall: Character escape asciiValue].	"Cursor keys clash with control keys: differentiate by sending 128+cursorKeyCode."	(char == 8 & deleteIsDel and: [evt controlKeyPressed not]) ifTrue: [char := 127].	(char < 32 and: [evt controlKeyPressed not]) ifTrue: [char := char + 128].	down downcall: char.	evt wasHandled: true.! !!TeletypeMorph methodsFor: 'events'!keyboardFocusChange: aBoolean	hasFocus := aBoolean! !!TeletypeMorph methodsFor: 'events'!lock	"We're losing keyboard focus."	super lock.	hasFocus := false.	self changed.! !!TeletypeMorph methodsFor: 'events'!mouseDown: evt	"A mouse button has been pressed.""	evt printString displayAt: 10@200."	evt hand newKeyboardFocus: self.	"Yellow button is menu (or selection send when in xterm mode)."	(evt yellowButtonChanged)		ifTrue:			[(mouseControlsSelection and: [evt anyModifierKeyPressed not])				ifTrue: [self sendSelection]				ifFalse: [(systemWindow isKindOf: TeletypeWindow)					ifTrue: [systemWindow offerWindowMenu]					ifFalse: [self offerVTMenu]].			 ^super mouseDown: evt].	"Red button is selection start (or selection extend when shifted in Squeak mode)."	(evt redButtonChanged)		ifTrue:			[evt shiftPressed				ifTrue: ["mouseControlsSelection not and:" self extendSelection: evt position]				ifFalse: [self startSelection: evt position].			 ^super mouseDown: evt].	"Blue button is selection extend when in xterm mode."	(mouseControlsSelection and: [evt blueButtonChanged])		ifTrue:			[self extendSelection: evt position.			 ^super mouseDown: evt].	super mouseDown: evt.! !!TeletypeMorph methodsFor: 'events'!mouseEnter: evt	"The pointer just entered the window."	TextCursor beCursor.	useScrollbar & scrollFlop ifTrue: [self showScrollbar].	super mouseEnter: evt! !!TeletypeMorph methodsFor: 'events'!mouseLeave: evt	"The cursor just left the window."	Cursor normal show.	useScrollbar & scrollFlop ifTrue: [self hideScrollbar].	super mouseLeave: evt! !!TeletypeMorph methodsFor: 'events'!mouseMove: evt	"The mouse is moving inside the window.""	evt printString displayAt: 10@220."	evt redButtonPressed ifTrue: [self trackSelection: evt position].	super mouseMove: evt! !!TeletypeMorph methodsFor: 'events'!mouseUp: evt	"A mouse button has been released.""	evt printString displayAt: 10@240."	evt redButtonChanged ifTrue: [self endSelection: evt position].	evt wasHandled: true! !!TeletypeMorph methodsFor: 'events'!unlock	"We're acquiring keyboard focus."	super unlock.	hasFocus := true.	self changed.! !!TeletypeMorph methodsFor: 'events'!wouldAcceptKeyboardFocus	"Of course we would."	^true! !!TeletypeMorph methodsFor: 'selection'!clearSelection	"Remove the visual representation of the selection region.  Saved selection text is unaffacted."	selectionActive ifFalse: [^self].	lines do: [:line | line clearSelection].	selectionActive := false.	"self changed"! !!TeletypeMorph methodsFor: 'selection'!copySelection	"Copy the currently selected text to the clipboard."	Clipboard clipboardText: selection! !!TeletypeMorph methodsFor: 'selection'!endSelection: screenPosition	"Mouse selection has just finished.  Stop tracking and, iff the mouse moved since selection start, compute and remember the new selection text."	self stopSteppingSelector: #trackSelection.	self showCursor; changed.	selectionEnd isNil ifTrue: [^nil].	self saveSelection.	screenPosition = mousePosition ifFalse: [selectionEnd := nil]! !!TeletypeMorph methodsFor: 'selection'!extendSelection: screenPosition	"Extend the current selection through screenPosition."	selectionStart isNil ifFalse: [self trackSelection: screenPosition]! !!TeletypeMorph methodsFor: 'selection'!getSelectionRegion	"Answer a Rectangle representing the current selection area in character coordinates.  Note that the origin may be to the right of the corner."	| start end tmp |	(start := selectionStart) y > (end := selectionEnd) y ifTrue: [start := selectionEnd.  end := selectionStart].	(start y == end y and: [start x > end x]) ifTrue: [tmp := start.  start := end.  end := tmp].	"start now guaranteed to be before end in screen"	"we don't include the character under selectionStart"	end := end - (1@0).	^Rectangle origin: start corner: end! !!TeletypeMorph methodsFor: 'selection'!highlightSelection	"The selection region has changed.  Update the visual representation."	| region |	region := self getSelectionRegion.	lines do: [ :line | line clearSelection].	region height == 0		"single line"		ifTrue:			[(lines at: region top) selectFrom: region left to: region right]		ifFalse:			[(lines at: region top) selectFrom: region left to: cols.			 lines from: region top + 1 to: region bottom - 1 do: [:line | line selectFrom: 1 to: cols].			 (lines at: region bottom) selectFrom: 1 to: region right].	selectionActive := true.	self changed! !!TeletypeMorph methodsFor: 'selection'!saveSelection	"A new selection has been made.  Compute and remember the selection text."	| stream region |	region := self getSelectionRegion.	stream := WriteStream on: String new.	lines from: region top to: region bottom do: [:line | line appendSelectionTo: stream].	selection := stream contents.	mouseControlsSelection ifTrue: [Clipboard clipboardText: selection].! !!TeletypeMorph methodsFor: 'selection'!selectLine: screenPosition	"Triple click.  Select the line surrounding the cursor, including the end of line, and save the selection text."	| pos |"	'select line   ' displayAt: 10@130."	pos := self selectionPositionAt: screenPosition.	selectionStart := 1 @ pos y.	selectionEnd := cols + 1 @ pos y.	self highlightSelection; saveSelection.	selectionEnd := nil.	mousePosition := nil.! !!TeletypeMorph methodsFor: 'selection'!selectWord: screenPosition	"Double click.  Select the word surrounding the cursor according to the current char class and save the selection text."	| pos line left right |"	'select word   ' displayAt: 10@130."	pos := self selectionPositionAt: screenPosition.	line := lines at: pos y.	left := line findFirstInClass: self characterClass from: (pos x min: cols).	right := line findLastInClass: self characterClass from: (pos x min: cols).	selectionStart := left @ pos y.	selectionEnd := right + 1 @ pos y.	right < left		ifTrue: [self clearSelection]		ifFalse: [self highlightSelection; saveSelection]! !!TeletypeMorph methodsFor: 'selection'!selectionPositionAt: screenPosition	"Answer a Point in character coordinates corresponding to the given position in screen coordinates.  If screenPosition is above the window, try to scroll up before answering the first character in the window.  If screenPosition is below the window, try to scroll down before answering one character right of the last character in the window."	| pos |	screenPosition y < (self submorphs at: 1) bounds top		ifTrue:			[self pageUp: (rows // 8 max: 1).			 ^1 @ (displayStart + 1)].	screenPosition y > (self submorphs at: rows) bounds bottom		ifTrue:			[self pageDown: (rows // 8 max: 1).			 ^(cols + 1) @ (displayStart + rows)].	1 to: rows do: [:i		| (pos := (self submorphs at: i) selectionColumnAt: screenPosition) isNil			ifFalse: [^pos @ (displayStart + i)]].	^nil! !!TeletypeMorph methodsFor: 'selection'!sendSelection	"Send the clipboard text to the application."	down downcallAll: Clipboard clipboardText asString asByteArray! !!TeletypeMorph methodsFor: 'selection'!startSelection: screenPosition	"Mouse selection has begun.  If this is a double click (mouse hasn't moved since last click and selectionEnd isNil) then select the word under the pointer.  If this is a triple click (mouse hasn't moved and selectionEnd notNil after prior word selection) then select the line under the pointer.  Otherwise clear the current selectionStart and begin tracking."	| start |	self hideCursor; changed.	mousePosition = screenPosition		ifTrue:			[^selectionEnd isNil				ifTrue: [self selectWord: screenPosition]				ifFalse: [self selectLine: screenPosition]]."	'mouse position ' , mousePosition printString,	' screen position ', screenPosition printString, 	' selectionEnd ', selectionEnd printString, 	'  ' displayAt: 10@150."	mousePosition := screenPosition.	selectionEnd := nil.	start := self selectionPositionAt: screenPosition.	self clearSelection.	selectionStart := start."	'selection begin ', selectionStart printString, '     ' displayAt: 10@10"! !!TeletypeMorph methodsFor: 'selection'!trackSelection	"The mouse is down during selection tracking.  Update the visual representation of the selected region."	| pos |	(pos := self selectionPositionAt: Sensor cursorPoint) isNil		ifFalse:			[selectionEnd := pos.			 self highlightSelection]! !!TeletypeMorph methodsFor: 'selection'!trackSelection: screenPosition	"The mouse moved during selection tracking.  Update the visual representation of the selected region."	(trackingSelection or: [(self selectionPositionAt: screenPosition) isNil])		ifFalse: [self				startStepping: #trackSelection				at: Time millisecondClockValue				arguments: #()				stepTime: 100]! !!TeletypeMorph methodsFor: 'geometry'!extent: newExtent	"We're being allocated real estate from above (initial placement or manual resize).  Adjust the screen size accordingly."	| layoutBounds textBounds nCols nRows org ext lineSkip |	super extent: newExtent.	layoutBounds := self layoutBounds.	textBounds := self textBounds: layoutBounds.	nCols := textBounds width // pitch.	nRows := textBounds height // skip.	self hideScrollbar; initTextBounds: textBounds width: nCols height: nRows.	org := textBounds topLeft.	ext := (cols * pitch) @ skip.	lineSkip := 0 @ skip.	submorphs doWithIndex: [:m :i |		m bounds: (org extent: ext).		org := org translateBy: lineSkip].	(useScrollbar and: [scrollFlop not]) ifTrue: [self showScrollbar].! !!TeletypeMorph methodsFor: 'geometry'!hideScrollbar	"Remove the scrollbar from the window."	scroll notNil		ifTrue:			[scroll delete.			 scroll := nil]! !!TeletypeMorph methodsFor: 'geometry'!initScrollbar: frame	"Initialize the scrollbar to fit in/around the given frame."	| width extent origin |	width := self scrollbarWidth.	scrollFlop		ifFalse:			[extent := (width) @ (frame height).			 origin := scrollRight				ifFalse: [frame origin]				ifTrue: [(frame right - width) @ (frame top)]]		ifTrue:			[extent := (width + borderWidth) @ (frame height + (borderWidth * 2)).			 origin := scrollRight				ifFalse: [(frame left - width - borderWidth) @ (frame top - borderWidth)]				ifTrue: [(frame right) @ (frame top - borderWidth)]].	scroll bounds: (origin extent: extent)! !!TeletypeMorph methodsFor: 'geometry'!initTextBounds: textBounds width: nCols height: nRows	"This is a geometry change imposed from outside (either initial placement or manual resizing).  We honour it without attempting to fix the geometry of our owner."	| r morphs morph |	(nCols == cols and: [nRows == rows]) ifTrue: [^self].	morphs := OrderedCollection withAll: submorphs.	"copy".	self clearSelection; hideCursor; removeAllMorphs.	cols := nCols max: 1.	x := x min: cols.	lines do: [ :line | line setWidth: cols].	r := nRows max: 1.	[rows < r]		whileTrue:			[displayStart > 0				ifTrue:					["suck last saved line back down into the screen"					 displayStart := displayStart - 1.					 savedLines := savedLines - 1.					 morphs addFirst: (SimpleTextMorph contents: (self displayLineAt: 1)).					 y := y + 1]				ifFalse:					["add a new empty line at the bottom of the screen"					 morph := SimpleTextMorph contents: (lines addLast: (SimpleTextState new: cols)).					 morphs addLast: morph].			 rows := rows + 1].	[rows > r]		whileTrue:			[rows := rows - 1.			 savedLines := savedLines + 1.			 displayStart := displayStart + 1.			 morphs removeLast.			 y := y - 1 max: 1].	self		addAllMorphs: morphs;		showCursor;		doSoftReset;		initializeTabs;		reportSizeToSession;		linesChanged.	"morphic explodes if we continue before making absolutely sure the window is redrawn..."	"(running and: [self world notNil]) ifTrue: [self world doOneCycle]"! !!TeletypeMorph methodsFor: 'geometry'!initialExtent	self flag: #ikp. "can this can go away?"	^self preferredExtent! !!TeletypeMorph methodsFor: 'geometry'!preferredExtent	"Answer the extent that we would be given in an ideal world (no pun intended).  This is just the amout of space we need to display our contents, and no more."	| w h s |	s := (useScrollbar and: [scrollFlop not]) ifTrue: [self scrollbarWidth] ifFalse: [0].	w := borderWidth + s + inset + (cols * pitch) + inset + borderWidth.	h := borderWidth + inset + (rows * skip) + inset + borderWidth.	^w@h! !!TeletypeMorph methodsFor: 'geometry'!scrollbarWidth	^ScrollPane new scrollbarWidth! !!TeletypeMorph methodsFor: 'geometry'!setWidth: nCols height: nRows	"This is a programmed geometry change.  We try to honour it by figuring out the corresponding geometry change required in our owner in order to acheive the given number of cols and rows.  The actual change takes place on the flip side, when our owner sends down our new extent."	| flak |	(cols == nCols and: [rows == nRows]) ifTrue: [^self].	flak := systemWindow isNil ifTrue: [0] ifFalse: [systemWindow fullBounds extent - self textBounds extent].	"self changed; sync."	(systemWindow isNil ifTrue: [self] ifFalse: [systemWindow])		extent: (pitch * nCols) @ (skip * nRows) + flak.	"self sync."! !!TeletypeMorph methodsFor: 'geometry'!showScrollbar	"Add a scrollbar to the window."	scroll isNil		ifTrue:			[scroll := ScrollBar new model: self slotName: 'scrollbar'; initializeEmbedded: scrollFlop not.			 self				addMorphBack: scroll;				initScrollbar: self layoutBounds;				updateScrollbar]! !!TeletypeMorph methodsFor: 'geometry'!textBounds	"Answer just the bounds of the text -- excluding border, scroll and inset."	^self textBounds: self layoutBounds! !!TeletypeMorph methodsFor: 'geometry'!textBounds: outer	"Answer just the bounds of the text -- excluding border, scroll and inset."	| width left right inner |	left := right := 0.	(useScrollbar & scrollFlop not)		ifTrue:			[width := self scrollbarWidth.			 scrollRight ifTrue: [right := width] ifFalse: [left := width]].	inner := outer insetBy: inset.	^(inner left + left) @ (inner top) corner: (inner right - right) @ (inner bottom)! !!TeletypeMorph methodsFor: 'character writing'!fillScreen: char	self		clearSelection;		linesDo: [:line | line atAllPut: char fg: fg bg: bg em: em]! !!TeletypeMorph methodsFor: 'character writing'!put: aChar	scrollOnOutput ifTrue: [self pageEnd].	(insertMode and: [x < cols]) ifTrue: [(self protectSelection; currentLine) insertAt: x].	(aChar == 13 & autoLinefeed or: [aChar == 10 & autoCR])		ifTrue: [self newline]		ifFalse: [self putNormal: aChar; cursorRight: 1 wrapping: true]! !!TeletypeMorph methodsFor: 'character writing'!putNormal: aChar	x >= cols ifTrue: [self wrapIfPossible].	selectionActive ifTrue: [self protectSelection].	self currentLine at: x put: (Character value: aChar) fg: fg bg: bg em: em! !!TeletypeMorph methodsFor: 'character writing'!resetVideo	self		clearSelection;		setBackground: 7;		setForeground: 0;		setEmphasis: 0! !!TeletypeMorph methodsFor: 'character writing'!setBackground: index	bg := index min: 7 max: 0.! !!TeletypeMorph methodsFor: 'character writing'!setEmphasis: index	index == 0 ifTrue: [^em := 0].	em := em bitOr: (1 bitShift: index - 1).! !!TeletypeMorph methodsFor: 'character writing'!setEmphasis: index to: bit	bit == 0		ifTrue: [em := em bitClear: (1 bitShift: index - 1)]		ifFalse: [em := em bitOr: (1 bitShift: index - 1)]! !!TeletypeMorph methodsFor: 'character writing'!setForeground: index	fg := index min: 7 max: 0! !!TeletypeMorph methodsFor: 'cursor control'!activePosition	^x@y! !!TeletypeMorph methodsFor: 'cursor control'!activePosition: aPoint	self hideCursor.	x := aPoint x min: cols max: 1.	y := aPoint y min: rows max: 1.	relativeOrigin ifTrue: [y := y + topLine - 1 min: bottomLine max: topLine].	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!bs	self cursorLeft: 1 wrapping: false! !!TeletypeMorph methodsFor: 'cursor control'!clearCursor	lines do: [ :line | line cursorCol: 0]! !!TeletypeMorph methodsFor: 'cursor control'!cr	self hideCursor.	x := 1.	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!cursorDown: n scrolling: scrollFlag	self hideCursor.	n timesRepeat: [self cursorDownScrolling: scrollFlag].	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!cursorLeft: n wrapping: wrapFlag	self hideCursor.	n timesRepeat: [self cursorLeftWrapping: wrapFlag].	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!cursorRight: n wrapping: wrapFlag	self hideCursor.	n timesRepeat: [self cursorRightWrapping: wrapFlag].	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!cursorUp: n scrolling: scrollFlag	self hideCursor.	n timesRepeat: [self cursorUpScrolling: scrollFlag].	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!hideCursor	self currentLine cursorCol: 0! !!TeletypeMorph methodsFor: 'cursor control'!lf	autoCR		ifTrue: [self newline]		ifFalse: [self cursorDown: 1 scrolling: true]! !!TeletypeMorph methodsFor: 'cursor control'!newline	self hideCursor; cursorDown: 1 scrolling: true.	x := 1.	self showCursor! !!TeletypeMorph methodsFor: 'cursor control'!showCursor	self currentLine cursorCol: x! !!TeletypeMorph methodsFor: 'operating modes'!doFullReset	"Clear the screen, reset tabs to every eight columns, and reset the terminal modes (such as wrap and smooth scroll) to their initial states just after terminal initialisation."	self		doSoftReset;		clearSelection;		clearScreen;		activePosition: 1@1;		initializeTabs;		initializeTerminalModes;		changed! !!TeletypeMorph methodsFor: 'operating modes'!doResetAndClear	"Reset the terminal to a sane state and clear the saved lines."	self doFullReset.	displayStart := 0.	savedLines := 0.	lines := lines copyFrom: lines size - rows + 1 to: lines size.	self linesChanged; changed! !!TeletypeMorph methodsFor: 'operating modes'!doSoftReset	"Reset scroll regions.  This can be convenient when some program has left the scroll regions set incorrectly (often a problem when using VMS or TOPS-20)."	topLine := 1.	bottomLine := rows.! !!TeletypeMorph methodsFor: 'operating modes'!metaSendsEscape: aBoolean	metaSendsEscape := aBoolean! !!TeletypeMorph methodsFor: 'operating modes'!setAltScreenColours: aBoolean	altScreenColours := aBoolean.	self setEmacsColours: ec.! !!TeletypeMorph methodsFor: 'operating modes'!setAutoLinefeed: aBoolean	autoLinefeed := aBoolean! !!TeletypeMorph methodsFor: 'operating modes'!setAutoWrap: aBoolean	autoWrap := aBoolean! !!TeletypeMorph methodsFor: 'operating modes'!setEmacsColours: aBoolean	(ec := aBoolean) & altScreenColours		ifTrue: [submorphs from: 1 to: rows do: [:line | line emacsColours]]		ifFalse: [submorphs from: 1 to: rows do: [:line | line normalColours]].	self changed! !!TeletypeMorph methodsFor: 'operating modes'!setIconTitle: aString	systemWindow notNil		ifTrue:			[(systemWindow isKindOf: TeletypeWindow)				ifTrue: [systemWindow setIconTitle: aString]]				"icon title ignored by other kinds of window"! !!TeletypeMorph methodsFor: 'operating modes'!setInsertMode: aBoolean	insertMode := aBoolean! !!TeletypeMorph methodsFor: 'operating modes'!setRelativeOrigin: aBoolean	relativeOrigin := aBoolean.	self activePosition: 1@1! !!TeletypeMorph methodsFor: 'operating modes'!setReverseVideo: aBoolean	reverseVideo == aBoolean ifTrue: [^self].	reverseVideo := aBoolean.	submorphs from: 1 to: rows do: [ :m | m rv: (rv xor: reverseVideo)].	self changed! !!TeletypeMorph methodsFor: 'operating modes'!setScrollRegionTop: top bottom: bottom	topLine := top min: rows - 1 max: 1.	bottomLine := bottom min: rows max: top + 1! !!TeletypeMorph methodsFor: 'operating modes'!setShowCursor: aBoolean	self hideCursor.	showCursor := aBoolean.	self showCursor! !!TeletypeMorph methodsFor: 'operating modes'!setSmoothScroll: aBoolean	autoFlush := 0.	smoothScroll := aBoolean! !!TeletypeMorph methodsFor: 'operating modes'!setWidth: nCols	allow132 ifTrue: [self setWidth: nCols height: rows]! !!TeletypeMorph methodsFor: 'operating modes'!setWindowTitle: aString	systemWindow notNil		ifTrue:			[(systemWindow isKindOf: TeletypeWindow)				ifTrue: [systemWindow setWindowTitle: aString]				ifFalse: [systemWindow setLabel: aString]]! !!TeletypeMorph methodsFor: 'editor functions'!clearLine	self		hideCursor;		clearLine: y from: 1 to: cols;		showCursor! !!TeletypeMorph methodsFor: 'editor functions'!clearLine: n from: l to: r	(self lineAt: n)		from: l to: r put: $  fg: fg bg: bg em: 0;		lastColumn: l - 1.! !!TeletypeMorph methodsFor: 'editor functions'!clearLineLeft	self		clearSelection;		hideCursor;		clearLine: y from: 1 to: x;		showCursor! !!TeletypeMorph methodsFor: 'editor functions'!clearLineRight	self		clearSelection;		hideCursor;		clearLine: y from: x to: cols;		showCursor! !!TeletypeMorph methodsFor: 'editor functions'!clearScreen	self hideCursor.	1 to: rows do: [:i | self clearLine: i from: 1 to: cols].	self showCursor.! !!TeletypeMorph methodsFor: 'editor functions'!clearScreenLeft	self		clearSelection;		hideCursor;		clearLine: y from: 1 to: x.	1 to: y - 1 do: [:i | self clearLine: i from: 1 to: cols].	self showCursor.	self flush! !!TeletypeMorph methodsFor: 'editor functions'!clearScreenRight	self		clearSelection;		hideCursor;		clearLine: y from: x to: cols.	y + 1 to: rows do: [:i | self clearLine: i from: 1 to: cols].	self showCursor.	self flush! !!TeletypeMorph methodsFor: 'editor functions'!deleteForward: n	self hideCursor.	n timesRepeat: [self deleteForward].	self showCursor! !!TeletypeMorph methodsFor: 'editor functions'!deleteLines: n	self hideCursor.	n timesRepeat: [self deleteLine].	self showCursor.	self autoFlush! !!TeletypeMorph methodsFor: 'editor functions'!insert: n	self hideCursor.	n timesRepeat: [self insert].	self showCursor! !!TeletypeMorph methodsFor: 'editor functions'!insertLines: n	self hideCursor.	n timesRepeat: [self insertLine].	self showCursor.	self autoFlush! !!TeletypeMorph methodsFor: 'tabs'!clearTab	x <= cols ifTrue: [tabs at: x put: false]."	Transcript		nextPutAll: 'tab CLR ', x printString; tab;		nextPutAll: (String withAll: (tabs collect: [:t | t ifTrue: [$!!] ifFalse: [$.]]));		cr; endEntry"! !!TeletypeMorph methodsFor: 'tabs'!clearTabs	tabs atAllPut: false.! !!TeletypeMorph methodsFor: 'tabs'!setTab	x <= cols ifTrue: [tabs at: x put: true]."	Transcript		nextPutAll: 'tab SET ', x printString; tab;		nextPutAll: (String withAll: (tabs collect: [:t | t ifTrue: [$!!] ifFalse: [$.]]));		cr; endEntry"! !!TeletypeMorph methodsFor: 'tabs'!tab	self hideCursor.	[x := x + 1.	 x < cols and: [(tabs at: x) not]]		whileTrue.	x := x min: cols.	self showCursor.! !!TeletypeMorph methodsFor: 'protocol'!down: protoLo	down := protoLo! !!TeletypeMorph methodsFor: 'protocol'!flush	submorphs from: 1 to: rows do: [ :m | m flush].	autoFlush := self autoFlushCount.	self updateScrollbar.! !!TeletypeMorph methodsFor: 'protocol'!install	session isNil ifFalse: [session propertyAt: #window put: self]! !!TeletypeMorph methodsFor: 'protocol'!isConnected	^session notNil and: [session isConnected]! !!TeletypeMorph methodsFor: 'protocol'!note: aSymbol with: anObject	aSymbol == #endpointClosed		ifTrue:			[^(systemWindow isKindOf: TeletypeWindow)				ifTrue: [systemWindow endpointClosed]].	aSymbol == #savePreferences ifTrue: [^self savePreferences: anObject].	aSymbol == #loadPreferences ifTrue: [^self loadPreferences: anObject].	aSymbol == #restoreDefaults ifTrue: 	[^self restoreDefaults]! !!TeletypeMorph methodsFor: 'protocol'!run	running := true.	session isNil ifFalse: [session note: #windowSize with: cols@rows]! !!TeletypeMorph methodsFor: 'protocol'!session	^session! !!TeletypeMorph methodsFor: 'protocol'!sessionNote: aSymbol	self sessionNote: aSymbol with: nil! !!TeletypeMorph methodsFor: 'protocol'!sessionNote: aSymbol with: anObject	session isNil ifFalse: [session note: aSymbol with: anObject]! !!TeletypeMorph methodsFor: 'protocol'!upcall: char"	Transcript show: 'upcall ', char printString; cr.""	char printString , '   ' displayAt: 10@10."	self put: char! !!TeletypeMorph methodsFor: 'protocol'!upcallAll: aCollection	aCollection do: [:b | self upcall: b]! !!TeletypeMorph methodsFor: 'menus'!buildMainMenu	| m |	(m := MenuMorph new) defaultTarget: self.	m	add: 'redraw window' target: owner action: #changed;			balloonTextForLastItem: 'Redisplay the contents of the window.';		addLine.	self		menu: m add: 'meta sends escape' var: metaSendsEscape eval: [metaSendsEscape := metaSendsEscape not]			help: 'Send ESC before each keyboard character if the command key is pressed.';		menu: m add: 'delete is del' var: deleteIsDel eval: [deleteIsDel := deleteIsDel not]			help: 'Send DEL instead of backspace.'.	m addLine.	m		add: 'character classes ...' action: #menuSetCharacterClasses;			balloonTextForLastItem: 'Modify the character classes used for double-click selection.'.	self		menu: m add: 'mouse controls clipboard' var: mouseControlsSelection eval: [mouseControlsSelection := mouseControlsSelection not]			help: 'If enabled then use xterm-style selection: selecting implies copy and the yellow button pastes.';		menu: m add: 'keyboard controls clipboard' var: keyboardControlsSelection eval: [keyboardControlsSelection := keyboardControlsSelection not]			help: 'If enabled then use Squeak-style selection: cmd-c copies selection to clipboard and cmd-v pastes.'.	m addLine.	m		add: 'saved line limit (', savedLineLimit printString, ') ...' action: #menuSetSavedLineLimit;			balloonTextForLastItem: 'Set the maximum number of lines to save beyond the top of the window.'.	m		add: 'screen size (', cols printString, 'x', rows printString, ') ...' action: #menuResizeScreen;			balloonTextForLastItem: 'Resize the screen to a given number of columns and rows.'.	^m! !!TeletypeMorph methodsFor: 'menus'!buildVTMenu	| m |	(m := MenuMorph new) defaultTarget: self.	self		menu: m add: 'enable scrollbar' var: useScrollbar eval: [self menuToggleScrollbar]			help: 'Show the scrollbar.';		menu: m add: 'enable jump scroll' var: smoothScroll not eval: [smoothScroll := smoothScroll not]			help: 'Allow several lines to be displayed at once when input is arriving quickly.';		menu: m add: 'enable reverse video' var: reverseVideo eval: [self setReverseVideo: reverseVideo not; flush]			help: 'Display the entire window in reverse video.';		menu: m add: 'enable auto wraparound' var: autoWrap eval: [autoWrap := autoWrap not]			help: 'Automatically wrap to the next line when the cursor reaches the last column.';		menu: m add: 'enable reverse wraparound' var: reverseWrap eval: [reverseWrap := reverseWrap not]			help: 'Automaticaly wrap to the previous line when the cursor moves left of the first column.';		menu: m add: 'enable auto linefeed' var: autoLinefeed eval: [autoLinefeed := autoLinefeed not]			help: 'Move to the next line automatically when carriage return is received.';		menu: m add: 'scroll to bottom on key press' var: scrollOnInput eval: [scrollOnInput := scrollOnInput not]			help: 'Automatically reposition the screen to the bottom of the scrolling region when keyboard input is received.';		menu: m add: 'scroll to bottom on tty output' var: scrollOnOutput eval: [scrollOnOutput := scrollOnOutput not]			help: 'Automatically reposition the screen to the bottom of the scrolling region when application output is received.';		menu: m add: 'allow 80/132 column switching' var: allow132 eval: [allow132 := allow132 not]			help: 'Allow the program to resize the window automatically using 80/132 column escape sequences.';		menu: m add: 'enable alternate screen switching' var: altScreenSwitch eval: [altScreenSwitch := altScreenSwitch not]			help: 'Allow switching to the alternate screen buffer.';		menu: m add: 'enable alternate screen colours' var: altScreenColours eval: [self setAltScreenColours: altScreenColours not]			help: self emacsColoursHelpString.	m addLine;		add: 'do soft reset' action: #doSoftReset;			balloonTextForLastItem: 'Reset the scroll regions.  This can be convenient when some program has left the scroll regions set incorrectly (often a problem when using VMS or TOPS-20).';		add: 'do full reset' action: #doFullReset;			balloonTextForLastItem: 'Clear the screen, reset tabs to every eight columns, and reset the terminal modes (such as wrap and smooth scroll) to their initial states just after terminal initialisation.';		add: 'reset and clear saved lines' action: #doResetAndClear;			balloonTextForLastItem: 'Perform a full reset and then forget all lines scrolled off the top of the window.'.	^m! !!TeletypeMorph methodsFor: 'menus'!menu: aMenu add: aString var: aBoolean eval: aBlock help: helpString	aMenu		add: (aBoolean ifTrue: ['<yes>'] ifFalse: ['<no>']), aString		selector: #menuBlock:		argument: aBlock;		balloonTextForLastItem: helpString! !!TeletypeMorph methodsFor: 'menus'!menuBlock: aBlock	aBlock value.	self flush! !!TeletypeMorph methodsFor: 'menus'!menuResizeScreen	| n c r |	n := FillInTheBlank		request: 'New screen size (columns x rows)?'		initialAnswer: cols printString, 'x', rows printString.	(n isNil or: [n isEmpty]) ifTrue: [^self].	n := n findTokens: 'x'.	n size == 2 ifFalse: [^Smalltalk beep].	(c := n first asInteger) isNil ifTrue: [^Smalltalk beep].	(r := n last asInteger) isNil ifTrue: [^Smalltalk beep].	self setWidth: c height: r! !!TeletypeMorph methodsFor: 'menus'!menuSetCharacterClasses	| n specs spec cclass range start stop |	n := FillInTheBlank request: 'Character classes?  (Enter ? for help.)' initialAnswer: '?'.	(n isNil or: [n isEmpty]) ifTrue: [^self].	n = '?' ifTrue: [^self menuSetCharacterClassesHelp].	specs := n findTokens: ','.	specs do: [ :aSpec |		Transcript cr; show: aSpec.		start := stop := cclass := nil.		spec := aSpec findTokens: ':'.		Transcript cr; show: spec printString.		spec size == 2			ifTrue:				[cclass := spec last asInteger.				 range := spec first findTokens: '-'.				 Transcript cr; show: range printString.				 (cclass notNil and: [range size >= 1 and: [range size <= 2]])					ifTrue:						[start := range first asInteger.						 stop := (range size == 2 ifTrue: [range last] ifFalse: [range first]) asInteger]].		stop isNil ifTrue: [^Smalltalk beep; inform: 'Could not parse: ', spec printString].		cclass := Character value: cclass.		characterClasses from: start + 1 to: stop + 1 put: cclass.].	(self confirm: 'Would you like the current character classes  to be the default?')		ifTrue: [CharClass := characterClasses]! !!TeletypeMorph methodsFor: 'menus'!menuSetCharacterClassesHelp	StringHolder new		contents: 'This preference is a series of comma-separated range:value pairs.  The range is either a single number or low-high in the range of 0 to 255, corresponding to the code for the character or characters to be set.  The value is the class in which to place the characters in the range.  The value is arbitrary, but the standard character classes use the following values: 1 for special graphics characters, 32 for whitespace, 48 for alphanumeric characters (letters and digits), and the code of the character itself for all others (corresponding to puncuation characters).For example,		33:48,37:48,45-47:48,64:48indicates that the exclamation mark, percent sign, dash, period, slash, and ampersand characters should be treated the same way as letters and digits.  This is useful for copying and pasting electronic mailing addresses and filenames.  Also,		33:48,37:48,45-47:48,58:48,64:48,126:48is similar but includes colon and tilde, making most URLs selectable with a double click.';		openLabel: 'Character Class Help'! !!TeletypeMorph methodsFor: 'menus'!menuSetSavedLineLimit	| n |	n := FillInTheBlank		request: 'Saved line count?'		initialAnswer: savedLineLimit printString.	(n := n asInteger) isNil ifTrue: [^self].	savedLineLimit := n.	(self confirm: 'Would you like to make ', n printString, ' lines the default?')		ifTrue: [SavedLineLimit := n]! !!TeletypeMorph methodsFor: 'menus'!menuToggleScrollbar	| scrollWidth |	self hideScrollbar.	useScrollbar := useScrollbar not.	scrollWidth := scrollFlop		ifTrue: [0]		ifFalse: [useScrollbar ifTrue: [self scrollbarWidth] ifFalse: [self scrollbarWidth negated]].	self initializeScrollbar.	systemWindow isNil		ifTrue: [self extent: self extent + (scrollWidth @ 0)]		ifFalse: [systemWindow extent: systemWindow extent + (scrollWidth @ 0)]! !!TeletypeMorph methodsFor: 'menus'!offerVTMenu	"The user has pressed CTRL-Yellow (button 2).  Offer a menu similar to the Xterm `VT' menu."	(self buildVTMenu addStayUpIcons title: 'VT Options')		popUpEvent: self currentEvent in: self world! !!TeletypeMorph methodsFor: 'scrolling'!hideOrShowScrollBar	"ScrollBar sends us this for no good reason."! !!TeletypeMorph methodsFor: 'scrolling'!pageDown: nLines	displayStart := displayStart + nLines min: lines size - rows.	self linesChanged; changed.! !!TeletypeMorph methodsFor: 'scrolling'!pageEnd	 lines size - rows == displayStart		ifFalse:		[displayStart := lines size - rows.		 self linesChanged; changed]! !!TeletypeMorph methodsFor: 'scrolling'!pageHome	displayStart := 0.	self linesChanged; changed.! !!TeletypeMorph methodsFor: 'scrolling'!pageUp: nLines	displayStart := displayStart - nLines max: 0.	self linesChanged; changed.! !!TeletypeMorph methodsFor: 'scrolling'!scrollbarMenuButtonPressed: evt	(systemWindow isKindOf: TeletypeWindow)		ifTrue: [systemWindow offerWindowMenu]		ifFalse: [self offerVTMenu]! !!TeletypeMorph methodsFor: 'scrolling'!scrollbarValue: value	| newStart |	newStart := (savedLines * value) rounded min: lines size - rows.	newStart == displayStart ifTrue: [^self].	displayStart := newStart.	self linesChanged; changed! !!TeletypeMorph methodsFor: 'scrolling'!updateScrollbar	scroll isNil ifTrue: [^self].	savedLines == 0 ifTrue: [^scroll interval: 1.0; setValue: 0].	scroll		scrollDelta: (1 / savedLines) asFloat		pageDelta: (rows / savedLines) asFloat;		interval: (rows / lines size) asFloat;		setValue: (displayStart / savedLines) asFloat! !!TeletypeMorph methodsFor: 'private'!autoFlush	(autoFlush := autoFlush - 1) < 1		ifTrue:			[self updateScrollbar; changed.			 autoFlush := self autoFlushCount.			 Processor yield.]! !!TeletypeMorph methodsFor: 'private'!autoFlushCount	^smoothScroll ifTrue: [2] ifFalse: [rows]! !!TeletypeMorph methodsFor: 'private'!banner	^''! !!TeletypeMorph methodsFor: 'private'!currentLine	^lines at: lines size - rows + y! !!TeletypeMorph methodsFor: 'private'!cursorDownScrolling: scrollFlag	(y >= bottomLine and: [scrollFlag]) ifTrue: [self scrollForward].	y := y + 1 min: bottomLine! !!TeletypeMorph methodsFor: 'private'!cursorLeftWrapping: wrapFlag	x > 1 ifTrue: [^x := x - 1].	(y > 1 and: [wrapFlag and: [reverseWrap]]) ifTrue: [x := cols.  y := y - 1]! !!TeletypeMorph methodsFor: 'private'!cursorRightWrapping: wrapFlag	((x := x + 1) > cols and: [wrapFlag not]) ifTrue: [x := cols].! !!TeletypeMorph methodsFor: 'private'!cursorUpScrolling: scrollFlag	(y <= topLine and: [scrollFlag]) ifTrue: [self scrollBackward].	y := y - 1 max: topLine! !!TeletypeMorph methodsFor: 'private'!debug: message	Transcript nextPutAll: message; cr; endEntry! !!TeletypeMorph methodsFor: 'private'!debug: message with: arg	Transcript nextPutAll: message; space; print: arg; cr; endEntry! !!TeletypeMorph methodsFor: 'private'!deleteForward	(self protectSelection; currentLine) deleteCharAt: x! !!TeletypeMorph methodsFor: 'private'!deleteLine	^self scrollForwardFrom: y to: bottomLine! !!TeletypeMorph methodsFor: 'private'!displayLineAt: index	"Answer the displayed (visible) line at index."	^lines at: displayStart + index! !!TeletypeMorph methodsFor: 'private'!displayLineAt: index put: anObject	"Change the displayed (visible) line at index."	^lines at: displayStart + index put: anObject! !!TeletypeMorph methodsFor: 'private'!displayLinesDo: aBlock	lines from: displayStart + 1 to: displayStart + rows do: aBlock! !!TeletypeMorph methodsFor: 'private'!drawOn: aCanvas	super drawOn: aCanvas.	self updateScrollbar.! !!TeletypeMorph methodsFor: 'private'!emacsColoursHelpString	"This is so long that it distracts from the menu building in progress."	^'Use Emacs-style colours in the alternate screen buffer.(Xterm has two independent screen buffers.  Programs such as "Emacs" and "less" run in the alternate buffer and switch back to the primary buffer when they exit -- hence preserving the original contents of the screen.  If this option is enabled then the alternate buffer will use a different colour scheme [similar to the one used by Emacs under X11] much better adapted to recent Emacsen [version 21 and higher] that are capable of using font-lock mode and colour highlighting when running in a terminal window.)'! !!TeletypeMorph methodsFor: 'private'!insert	(self protectSelection; currentLine)		at: x insert: $  fg: fg bg: bg em: em! !!TeletypeMorph methodsFor: 'private'!insertLine	^self scrollBackwardFrom: y to: bottomLine! !!TeletypeMorph methodsFor: 'private'!lineAt: index	^lines at: savedLines + index! !!TeletypeMorph methodsFor: 'private'!linesChanged	| lineNo line |	lineNo := displayStart.	1 to: rows do: [ :i |		line := lines at: (lineNo := lineNo + 1).		line setWidth: cols.		(submorphs at: i) lineState: line].	1 to: rows do: [ :i | (submorphs at: i) rv: rv; cursorColour: cursorColour; ec: ec & altScreenColours].	self autoFlush! !!TeletypeMorph methodsFor: 'private'!linesDo: aBlock	self linesFrom: 1 to: rows do: aBlock! !!TeletypeMorph methodsFor: 'private'!linesFrom: start to: stop do: aBlock	| first |	first := lines size - rows.	lines from: first + start to: first + stop do: aBlock! !!TeletypeMorph methodsFor: 'private'!loadPreferences: dict	| r c s |	(dict at: #saveTerminalMainOptions)		ifTrue:			[metaSendsEscape := dict at: #metaSendsEscape.			 deleteIsDel := dict at: #deleteIsDel.			 savedLineLimit := dict at: #savedLineLimit.			 characterClasses := (dict at: #characterClasses) copy.			 mouseControlsSelection := dict at: #mouseControlsSelection.			 keyboardControlsSelection := dict at: #keyboardControlsSelection.			 savedLineLimit := dict at: #savedLineLimit].	(dict at: #saveTerminalVTOptions)		ifTrue:			[s  := dict at: #useScrollbar.			 s == useScrollbar ifFalse: [self menuToggleScrollbar].			 smoothScroll := dict at: #smoothScroll.			 reverseVideo := dict at: #reverseVideo.			 autoWrap := dict at: #autoWrap.			 reverseWrap := dict at: #reverseWrap.			 autoLinefeed := dict at: #autoLinefeed.			 autoCR := dict at: #autoCR.			 scrollOnInput := dict at: #scrollOnInput.			 scrollOnOutput := dict at: #scrollOnOutput.			 allow132 := dict at: #allow132.			 altScreenSwitch := dict at: #altScreenSwitch.			 altScreenColours := dict at: #altScreenColours].	(dict at: #saveTerminalSize)		ifTrue:			[r := dict at: #rows.			 c := dict at: #cols.			 (r == rows and: [c == cols]) ifFalse: [self setWidth: c height: r]]! !!TeletypeMorph methodsFor: 'private'!newLine	| line |	line := (SimpleTextState string: (String new: cols withAll: $ )) .	ec & altScreenColours ifTrue: [line emacsColours].	^line! !!TeletypeMorph methodsFor: 'private'!protectSelection	"If the active position is within the selected region, clear the selection."	self flag: #ikp. "This is called way too often.  Need to go look where and when it's really necessary.""	'protect selection ', (TEMP := TEMP + 1) printString, '  ' displayAt: 10@170."	selectionActive ifTrue: [(self currentLine selectionSpansColumn: x) ifTrue: [self clearSelection]]! !!TeletypeMorph methodsFor: 'private'!reportSizeToSession	session isNil ifFalse: [session note: #windowSize with: cols@rows]! !!TeletypeMorph methodsFor: 'private'!restoreDefaults	self initializeTerminalModes.	savedLineLimit := SavedLineLimit.	altScreenColours ifTrue: [self setAltScreenColours: false].	mouseControlsSelection := true.	keyboardControlsSelection := false.	scrollOnInput := false.	scrollOnOutput := true.	allow132 := true.	characterClasses := CharClass copy.	useScrollbar ifTrue: [self menuToggleScrollbar].	(cols == 80 and: [rows == 24]) ifFalse: [self setWidth: 80 height: 24]! !!TeletypeMorph methodsFor: 'private'!savePreferences: dict	dict		at: #rows put: rows;		at: #cols put: cols;		at: #savedLineLimit put: savedLineLimit;		at: #useScrollbar put: useScrollbar;		at: #autoWrap put: autoWrap;		at: #reverseWrap put: reverseWrap;		at: #autoLinefeed put: autoLinefeed;		at: #autoCR put: autoCR;		at: #smoothScroll put: smoothScroll;		at: #metaSendsEscape put: metaSendsEscape;		at: #deleteIsDel put: deleteIsDel;		at: #altScreenSwitch put: altScreenSwitch;		at: #altScreenColours put: altScreenColours;		at: #reverseVideo put: reverseVideo;		at: #mouseControlsSelection put: mouseControlsSelection;		at: #keyboardControlsSelection put: keyboardControlsSelection;		at: #scrollOnInput put: scrollOnInput;		at: #scrollOnOutput put: scrollOnOutput;		at: #allow132 put: allow132;		at: #characterClasses put: characterClasses copy! !!TeletypeMorph methodsFor: 'private'!scrollBackward	^self scrollBackwardFrom: topLine to: bottomLine! !!TeletypeMorph methodsFor: 'private'!scrollBackwardFrom: top to: bot	self hideCursor.	savedLines + bot to: savedLines + top + 1 by: -1 do: [ :i | lines at: i put: (lines at: i - 1)].	lines at: savedLines + top put: (SimpleTextState new: cols).	self showCursor.	self linesChanged! !!TeletypeMorph methodsFor: 'private'!scrollForward	^self scrollForwardFrom: topLine to: bottomLine! !!TeletypeMorph methodsFor: 'private'!scrollForwardFrom: top to: bot	self hideCursor.	(top == 1 and: [bot == rows and: [altScreenActive not]])		ifTrue:			[lines addLast: (SimpleTextState new: cols).			 lines size > (savedLineLimit + rows)				ifTrue: [lines removeFirst selection notNil ifTrue: [self clearSelection]]				ifFalse:					[savedLines := savedLines + 1.					 displayStart := displayStart + 1]]		ifFalse:			[savedLines + top to: savedLines + bot - 1 do: [ :i | lines at: i put: (lines at: i + 1)].			 lines at: savedLines + bot put: (SimpleTextState new: cols)].	self showCursor.	self linesChanged; autoFlush! !!TeletypeMorph methodsFor: 'private'!step	steps := steps + 1! !!TeletypeMorph methodsFor: 'private'!sync	"Wait until the world has revolved at least once before proceeding."	| s |	s := steps + 1.	[steps < s] whileTrue: [^Processor yield]! !!TeletypeMorph methodsFor: 'private'!wrapIfPossible	x > cols ifTrue: [autoWrap		ifTrue: [x := 1.  self cursorDown: 1 scrolling: true]		ifFalse: [x := cols]]! !!TeletypeMorph class methodsFor: 'class initialization'!initialize	"TeletypeMorph initialize"	self initializeCursor.	self initializeCharacterClasses.	SavedLineLimit := 64.	"Set the default selection handling behaviour."	MouseControlsSelection := true.	KeyboardControlsSelection := false.	"Notes:	If MouseControlsSelection then selected text is copied to the clipboard when selection tracking finishes and the yellow button pastes from the clipboard into the terminal window.  Note that this moves the VT Options menu off the yellowButton and onto control-yellowButton (which is usually bound to the morph menu by things beyond our control; i.e., the VT menu becomes unavailable from within the TtyMorph itself.).	If KeyboardControlsSelection then cmd-c copies the last mouse selection to the clipboard and cmd-v pastes the clipboard text into the terminal.  Note that this means M-C and M-V will no longer be available to programs such as Emacs.	Note also that these can be in effect simultaneously to have both mouse and keyboard control of the selection.	By default we turn mouse selection on and keyboard selection off.  This way the TtyMorph responds to the mouse like a real xterm and the M-C and M-V keys are passed unhindered to Emacs."! !!TeletypeMorph class methodsFor: 'class initialization'!initializeCharacterClasses	"TeletypeMorph initializeCharacterClasses asByteArray inspect"	"Clicking the red button twice in rapid succession will cause all adjacent characters of the same class (e.g., letters, white space, punctuation) under the pointer to be selected.  Since different people have different preferences for what should be selected (for example, whether filenames should be selected as a whole or as individual path components) the default mapping can be overridden through the use of the CharClass variable.  The default collects whitespace, alphanumeric and special graphics characters into three classes.  Each punctuation character is in a class of its own.	NOTE: in order to see the contents of some of the strings in this method you might have to select their contents and then use the window menu to change their font to Fixed."	"Start with each character in its own class."	CharClass := (0 to: 255) asByteArray asString.	"Put the ASCII whitespace characters (nul tab space) into the same class as space."	#(0 9 32)		do: [:c | CharClass at: 1 + c put: $ ].	"Put the ASCII alphanumeric characters into the same class as `0'."	'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ:=abcdefghijklmnopqrstuvwxyz'		do: [:c | CharClass at: 1 + c asciiValue put: $0].	"Put the ISO 8859 Latin-1 accented characters into the same class as `0'."	'¿¡¬√ƒ≈Δ«»… ÀÃÕŒœ–—“”‘’÷ÿŸ⁄€‹›ﬁﬂ‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ¯˘˙˚¸˝˛ˇ'		do: [:c | CharClass at: 1 + c asciiValue put: $0].	"Put the ANSI special graphics characters into the same class as SOH (char 1)."	'ÄÅÇÉÑÖÜáàâäãåçéèêëíìîïñóòôöõúùûü'		do: [:c | CharClass at: 1 + c asciiValue put: (Character value: 1)].	"All other characters are punctuation and remain singletons."	^CharClass	"The table below is pulled directly out of xterm.  Evaluate the following to yield an Array whose first element is the complete set of xterm char classes and whose second element contains just the three non-singleton classes:	| s |	s := IdentityDictionary new.	#(32 1 1 1 1 1 1 1 1 32 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 32	   33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 48 48 48 48 48 48 48	   48 48 58 59 60 61 62 63 64 48 48 48 48 48 48 48 48 48 48 48 48 48 48	   48 48 48 48 48 48 48 48 48 48 48 48 91 92 93 94 48 96 48 48 48 48 48	   48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 123 124	   125 126 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1	   1 1 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175	   176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 48 48	   48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 215 48	   48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48	   48 48 48 48 48 48 48 247 48 48 48 48 48 48 48 48)		withIndexDo: [:e :i | (s at: e ifAbsent: [s at: e put: IdentitySet new]) add: i - 1].	s := s associationsDo: [:a | a value: a value asSortedCollection asByteArray].	^Array with: s with: (s select: [:v | v size > 1])	"! !!TeletypeMorph class methodsFor: 'class initialization'!initializeCursor	"TeletypeMorph initializeCursor"	"TextCursor showWhile: [Sensor waitButton]"	TextCursor :=   CursorWithMask derivedFrom:		(Cursor			extent: 16@16			fromArray: #(		2r0000000000000000		2r0000111011100000		2r0000001110000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000000100000000		2r0000001110000000		2r0000111011100000		2r0000000000000000		2r0)			offset: -7@-7).! !!TeletypeMorph class methodsFor: 'class initialization'!showCharacterClasses	"TeletypeMorph showCharacterClasses"	| s range start stop |	s := IdentityDictionary new.	CharClass asByteArray withIndexDo: [:e :i | (s at: e ifAbsent: [s at: e put: IdentitySet new]) add: i - 1].	s := (s associationsDo: [ :a | a value: a value asSortedCollection asByteArray]) select: [:v | v size > 1].	s := String streamContents: [ :str |		s keys asSortedCollection do: [ :key |			range := ReadStream on: (s at: key).			[range atEnd] whileFalse:				[str nextPut: $,.start := range next.				 stop := start.				 [range atEnd not and: [stop + 1 == range peek]] whileTrue: [stop := range next].				 str nextPutAll: start printString.				 start == stop ifFalse: [str nextPut: $-; nextPutAll: stop printString].				 str nextPut: $:; nextPutAll: key printString]]].	s := s copyFrom: 2 to: s size.	FillInTheBlank		request: 'Character classes'		initialAnswer: s! !!TeletypeMorph class methodsFor: 'instance creation'!new	| tty |	tty := super new initialize: 80@24.	^tty! !!TeletypeMorph class methodsFor: 'examples'!example	"TeletypeMorph example"	| echo tty |	"Pushing tty onto localEcho will convert ASCII control chars into tty cursor commands."	(echo := ProtocolAdaptor new) localEcho asProtocolStack push: (tty := self new openInHand); install; run.	echo downcallAll: TeletypeMorph organization classComment string asByteArray.	tty activePosition: 1@24; flush.	^tty! !!TeletypeMorph class methodsFor: 'examples'!example2	"TeletypeMorph example2"	| top tty |	top := ScrollPane new scroller: (tty := TeletypeMorph new).	ProtocolAdaptor new localEcho asProtocolStack push: tty; install; run.	top openInHand! !!TeletypeWindow methodsFor: 'initialize-release'!initialize	super initialize.	self setLabel: (windowTitle := iconTitle := 'Teletype').	self extent: 10@10.! !!TeletypeWindow methodsFor: 'initialize-release'!openOn: ttyMorph	"Open a new SystemWindow containing just a ttyMorph."	| ext |	self setLabel: windowTitle.	tty := ttyMorph setWindow: self.	self		addMorph: tty frame: (0 @ 0 corner: 1 @ 1);		model: tty.	"Figure out what our extent should be based on how much extra space we use for decoration."	ext := tty preferredExtent						"the extent that the tty would like to receive"		+ self extent - self ttyLayoutBounds extent.	"window decoration"	self activeHand keyboardFocus: nil.			"make sure we get focus when we're opened"	self extent: ext; openInWorldExtent: ext.	tty install; run! !!TeletypeWindow methodsFor: 'initialize-release'!openOn: ttyMorph labeled: aString	"Open a new SystemWindow containing just a ttyMorph."	windowTitle := iconTitle := aString.	^self openOn: ttyMorph! !!TeletypeWindow methodsFor: 'accessing'!setIconTitle: aString	iconTitle := aString.	self isCollapsed ifTrue: [self setLabel: aString].! !!TeletypeWindow methodsFor: 'accessing'!setWindowTitle: aString	windowTitle := aString.	self isCollapsed ifFalse: [self setLabel: aString].! !!TeletypeWindow methodsFor: 'connections'!closeConnection	(self confirm: 'Really disconnect?') ifTrue: [tty session endpoint close]! !!TeletypeWindow methodsFor: 'connections'!endpointClosed	AutoClose ifTrue: [^self delete].	self setLabel: (windowTitle := iconTitle := 'disconnected').	(ProtocolAdaptor new localEcho) asProtocolStack push: tty; install; run! !!TeletypeWindow methodsFor: 'connections'!newConnection	| remote host port |	remote := FillInTheBlank		request: 'Remote host name and port?'		initialAnswer: 'localhost 23'.	(remote isNil or: [remote size == 0]) ifTrue: [^nil].	remote := remote substrings.	remote size < 1 ifTrue: [^nil].	host := remote first.	port := remote size > 1 ifTrue: [remote last asInteger].	port isNil ifTrue: [port := 23].	self newConnection: host port: port! !!TeletypeWindow methodsFor: 'connections'!newConnection: args	^self newConnection: args preferences: nil! !!TeletypeWindow methodsFor: 'connections'!newConnection: hostName port: portNumber	| ep |	tty upcallAll: ('Trying ', hostName, '...') asByteArray; newline; flush.	ep := NetworkEndpoint newConnection: hostName port: portNumber.	ep isNil ifTrue: [^self error: 'Could not connect'].	self setIconTitle: hostName; setWindowTitle: hostName.	^ep asProtocolStack		push:		TelnetProtocol new;		push:		TerminalType new;		push:		tty;		install;		run! !!TeletypeWindow methodsFor: 'connections'!newConnection: args preferences: prefs	| hostName portNumber ep params |	hostName := args first.	portNumber := args last.	params := prefs isNil ifTrue: [IdentityDictionary new] ifFalse: [prefs].	tty upcallAll: ('Trying ', hostName, '...') asByteArray; newline; flush.	ep := NetworkEndpoint newConnection: hostName port: portNumber.	ep isNil ifTrue: [^self error: 'Could not connect'].	self setIconTitle: hostName; setWindowTitle: hostName.	^ep asProtocolStack		push: TelnetProtocol new;		push: TerminalType new;		push: tty;		note: #loadPreferences with: params;		install;		run! !!TeletypeWindow methodsFor: 'connections'!newShell	| shell argv prog |	shell := FillInTheBlank		request: 'Shell command?'		initialAnswer: '/bin/bash -i'.	argv := shell substrings.	argv isEmpty ifTrue: [^self].	prog := argv first.	argv := argv copyFrom: 2 to: argv size.	self setIconTitle: prog; setWindowTitle: prog.	(ProcessEndpoint command: prog arguments: argv) asProtocolStack		push:		TerminalType new;		push:		tty;		install;		run! !!TeletypeWindow methodsFor: 'connections'!newShell: args	^self newShell: args preferences: nil! !!TeletypeWindow methodsFor: 'connections'!newShell: args preferences: prefs	| argv prog params |	prog := args first.	argv := args last.	params := prefs isNil ifTrue: [IdentityDictionary new] ifFalse: [prefs].	self setIconTitle: prog; setWindowTitle: prog.	(ProcessEndpoint command: prog arguments: argv) asProtocolStack		push: TerminalType new;		push: tty;		note: #loadPreferences with: params;		install;		run! !!TeletypeWindow methodsFor: 'connections'!newShortcut: shortcut	| epClass epArgs |	self savePreferences: shortcut.	epClass := shortcut at: #endpointClass.	epArgs := shortcut at: #endpointArguments.	epClass == #ProcessEndpoint ifTrue: [^self newShell: epArgs preferences: shortcut].	epClass == #NetworkEndpoint ifTrue: [^self newConnection: epArgs preferences: shortcut].	self error: 'I don''t know how to create an endpoint of type ', epClass! !!TeletypeWindow methodsFor: 'events'!collapseOrExpand	super collapseOrExpand.	self setLabel: (self isCollapsed ifTrue: [iconTitle] ifFalse: [windowTitle])! !!TeletypeWindow methodsFor: 'events'!delete	(tty isConnected not or: [self confirm: 'There is a session in progress.Would you like to disconnect and close?(Answering no will abandon the close request.)'])		ifFalse: [^self].	tty delete.	^super delete! !!TeletypeWindow methodsFor: 'events'!doFastWindowReframe: ptName	"Override to provide geometry feedback at display top left and to ensure sane gridding of final bounds."	| newBounds delta flak w pitch skip grid h |	"For fast display, only higlight the rectangle during loop"	flak := self bounds extent - tty textBounds extent.	pitch := tty pitch.	skip := tty skip.	grid := pitch@skip.	newBounds := self bounds newRectButtonPressedDo: [:f | 		delta := f extent - flak grid: grid.		w := delta x // pitch.		h := delta y // skip.		delta := delta + flak.		((' ', w printString, 'x', h printString, ' ') forceTo: 10 paddingWith: $ ) displayAt: 5@5.		f 			withSideOrCorner: ptName			setToPoint: (self pointFromWorld: Sensor cursorPoint)			minExtent: self minimumExtent].	delta := (newBounds extent - flak grid: grid) + flak.	self bounds: (newBounds origin extent: delta).	self world invalidRect: (5@5 extent: (TextMorph new contents: '999x999') extent)! !!TeletypeWindow methodsFor: 'events'!extent: aPoint	super extent: aPoint.	self isCollapsed ifTrue: [^self].	tty notNil ifTrue: [tty extent: self ttyLayoutBounds extent].! !!TeletypeWindow methodsFor: 'events'!qdoFastWindowReframe: ptName	"Override to provide geometry feedback at display top left and to ensure sane gridding of final bounds."	| newBounds delta flak w pitch skip grid h |	"For fast display, only higlight the rectangle during loop"	flak := self bounds extent - tty textBounds extent.	pitch := tty pitch.	skip := tty skip.	grid := pitch@skip.	newBounds := self bounds newRectButtonPressedDo: [:f | 		delta := f extent - flak grid: grid.		w := delta x // pitch.		h := delta y // skip.		delta := delta + flak.		((' ', w printString, 'x', h printString, ' ') forceTo: 10 paddingWith: $ ) displayAt: 5@5.		f 			withSideOrCorner: ptName			setToPoint: (self pointFromWorld: Sensor cursorPoint)			minExtent: self minimumExtent].	delta := (newBounds extent - flak grid: grid) + flak.	self bounds: (newBounds origin extent: delta).	self world invalidRect: (5@5 extent: (TextMorph new contents: '999x999') extent).	self activeHand newKeyboardFocus: self! !!TeletypeWindow methodsFor: 'menus-window'!addConnectMenuTo: aMenu	| connectMenu removeMenu value |	Shortcuts isEmpty		ifFalse:			[connectMenu := MenuMorph new defaultTarget: self.			 removeMenu := MenuMorph new defaultTarget: self.			 Shortcuts keys asSortedCollection do: [ :key |				value := Shortcuts at: key.				connectMenu add: key selector: #newShortcut: argument: value.				connectMenu balloonTextForLastItem: 'Open a session for ', (value at: #endpointArguments) first printString, '.'.				removeMenu add: key selector: #removeShortcut: argument: key.				removeMenu balloonTextForLastItem: 'Delete the shorcut for ', (value at: #endpointArguments) first printString, '.'].			 connectMenu addLine.			 connectMenu add: 'remove shortcut ' subMenu: removeMenu.			 connectMenu balloonTextForLastItem: 'Delete a shortcut.'.			 connectMenu add: 'clear shortcuts' selector: #menuBlock: argument: [Shortcuts := Dictionary new].			 connectMenu balloonTextForLastItem: 'Delete all shortcuts.'.			 aMenu add: 'shortcuts ' subMenu: connectMenu.			 aMenu balloonTextForLastItem: 'Open a session from a previously saved shortcut.'].	aMenu add: 'new shell session ...' action: #newShell;		balloonTextForLastItem: 'Run a new local program in this window.'.	aMenu add: 'new telnet session ...' action: #newConnection;		balloonTextForLastItem: 'Open a new telnet connection in this window.'.! !!TeletypeWindow methodsFor: 'menus-window'!buildWindowMenu	| aMenu |	aMenu := MenuMorph new defaultTarget: self.	tty isConnected		ifTrue:			[aMenu add: 'disconnect' action: #closeConnection.			 aMenu balloonTextForLastItem: 'Shut down the current session.'.			 aMenu add: 'save shortcut ...' action: #menuSaveShortcut.			 aMenu balloonTextForLastItem: 'Save (or modify) a shortcut for the current session.']		ifFalse:			[self addConnectMenuTo: aMenu].	aMenu addLine		add: 'new window' selector: #menuBlock: argument: [TeletypeWindow open];		balloonTextForLastItem: 'Open a new, independent teletype window.'.	tty isConnected		ifTrue: [aMenu add: 'disconnect and close' action: #delete;			 		balloonTextForLastItem: 'Shut down the current session and close the window.'.]		ifFalse: [aMenu add: 'close window' action: #delete;			 		balloonTextForLastItem: 'Close the window.'.].	aMenu addLine;		add: 'main options' subMenu: tty buildMainMenu;		balloonTextForLastItem: 'Modify generalt terminal options.';		add: 'vt options ' subMenu: tty buildVTMenu;		balloonTextForLastItem: 'Modify VT100 emulation options.';		add: 'telnet options ' subMenu: TelnetProtocol buildOptionsMenu;		balloonTextForLastItem: 'Modify telnet protocol options.'.	aMenu addLine;		add: 'preferences ' subMenu: self buildConfigMenu;		balloonTextForLastItem: 'Set preferences for shell and telnet sessions.'.	aMenu addLine;		add: 'window ' subMenu: super buildWindowMenu;		balloonTextForLastItem: 'Modify the characteristics of this window.'.	^aMenu! !!TeletypeWindow methodsFor: 'menus-window'!menuSaveShortcut	| shortcut name | 	shortcut := IdentityDictionary new.	shortcut at: #sessionType put: #unknown.	tty sessionNote: #savePreferences with: shortcut.	name := FillInTheBlank request: 'Shortcut name?' initialAnswer: 'Untitled'.	(name isNil or: [name isEmpty]) ifTrue: [^self].	((Shortcuts includesKey: name) and: [(self confirm: 'Replace existing shortcut called `', name, '''?') not])		ifTrue: [^self].	Shortcuts at: name put: shortcut.! !!TeletypeWindow methodsFor: 'menus-window'!offerWindowMenu	| aMenu |	aMenu := self buildWindowMenu.	aMenu popUpEvent: self currentEvent in: self world! !!TeletypeWindow methodsFor: 'menus-window'!removeShortcut: name	Shortcuts removeKey: name ifAbsent: []! !!TeletypeWindow methodsFor: 'menus-config'!buildConfigMenu	| m |	m := MenuMorph new defaultTarget: self.	m		add: 'terminal type (', TerminalType terminalTypeName, ') '		subMenu: ((MenuMorph new defaultTarget: self)			add: 'vt102' selector: #menuBlock: argument: [TerminalType := VT102Emulator];			balloonTextForLastItem: 'Emulate a VT102 terminal.';			add: 'xterm' selector: #menuBlock: argument: [TerminalType := XtermEmulator];			balloonTextForLastItem: 'Emulate an xterm terminal.');		balloonTextForLastItem: 'Select the kind of terminal emulation to use.'.	m		add: 'font (', SimpleTextMorph defaultFont, ')' subMenu: self buildFontMenu;		balloonTextForLastItem: 'Set the default font for terminal windows.(Changes take effect in NEW windows, not this one.)'.	self menu: m add: 'enable auto-close' var: AutoClose eval: [AutoClose := AutoClose not]			help: 'Automatically close the window when the session terminates.'.	m addLine.	self menu: m add: 'save screen size' var: SaveTerminalSize			eval: [SaveTerminalSize := SaveTerminalSize not]			help: 'Save terminal screen size in shortcuts and restore it in new sessions.'.	self menu: m add: 'save main options' var: SaveTerminalMainOptions			eval: [SaveTerminalMainOptions := SaveTerminalMainOptions not]			help: 'Save terminal main options in shortcuts and restore them in new sessions.'.	self menu: m add: 'save vt options' var: SaveTerminalVTOptions			eval: [SaveTerminalVTOptions := SaveTerminalVTOptions not]			help: 'Save terminal VT options in shortcuts and restore them in new sessions.'.	self menu: m add: 'save telnet options' var: SaveTelnetOptions			eval: [SaveTelnetOptions := SaveTelnetOptions not]			help: 'Save telnet protocol options in shortcuts and restore them in new sessions.'.	m addLine; add: 'restore defaults' selector: #menuBlock: argument: [self menuRestoreDefaults];		balloonTextForLastItem: 'Reset ALL preferences and option values to their factory settings.'.	^m! !!TeletypeWindow methodsFor: 'menus-config'!buildFontMenu	| m |	m := MenuMorph new defaultTarget: SimpleTextMorph.	StrikeFont monospacedFamilyNames do: [ :family |		m add: family		selector: #defaultFont:		argument: family;		 balloonTextForLastItem: 'Set the default font to ', family, '.(Changes take effect in NEW windows, not this one.)'	].	^ m! !!TeletypeWindow methodsFor: 'menus-config'!menuRestoreDefaults	(self confirm: 'Really restore all defaults? Note: this will also clear all of your shortcuts!! ') ifFalse: [^self].	TeletypeWindow initialize.	TeletypeMorph initialize.	VT102Emulator initialize.	XtermEmulator initialize.	TelnetProtocol initialize.	tty sessionNote: #restoreDefaults	! !!TeletypeWindow methodsFor: 'private'!menu: aMenu add: aString var: aBoolean eval: aBlock help: helpString	aMenu		add: (aBoolean ifTrue: ['<yes>'] ifFalse: ['<no>']), aString		selector: #menuBlock:		argument: aBlock;		balloonTextForLastItem: helpString! !!TeletypeWindow methodsFor: 'private'!menuBlock: aBlock	aBlock value! !!TeletypeWindow methodsFor: 'private'!savePreferences: dict	dict		at: # saveTerminalSize put: SaveTerminalSize;		at: # saveTerminalMainOptions put: SaveTerminalMainOptions;		at: # saveTerminalVTOptions put: SaveTerminalVTOptions;		at: # saveTelnetOptions put: SaveTelnetOptions! !!TeletypeWindow methodsFor: 'private'!setLabel: aString	labelString = aString ifFalse: [super setLabel: aString]! !!TeletypeWindow methodsFor: 'private'!ttyLayoutBounds	^self layoutBounds! !!TeletypeWindow class methodsFor: 'class initialization'!initialize	"TeletypeWindow initialize"	AutoClose := false.	SaveTelnetOptions := false.	SaveTerminalMainOptions := true.	SaveTerminalSize := false.	SaveTerminalVTOptions := true.	TerminalType := XtermEmulator.	Shortcuts := Dictionary new.	! !!TeletypeWindow class methodsFor: 'instance creation'!new	^super new initialize! !!TeletypeWindow class methodsFor: 'instance creation'!open	^self new openOn: TeletypeMorph new! !!TeletypeWindow class methodsFor: 'instance creation'!telnet: hostName	"TeletypeWindow telnet: 'localhost'"	^self telnet: hostName port: 23! !!TeletypeWindow class methodsFor: 'instance creation'!telnet: hostName port: portNumber	"TeletypeWindow telnet: 'localhost' port: 23"	| ep tty |	ep := NetworkEndpoint newConnection: hostName port: portNumber.	ep isNil ifTrue: [^self error: 'Could not connect'].	self new openOn: (tty := TeletypeMorph new).	ep asProtocolStack		push:	TelnetProtocol new;		push:	TerminalType new;		push:	tty;		install;		run! !!TelnetProtocol methodsFor: 'initialize-release'!initialState: aState	super initialState: aState.	self		setDebug: Debug;		setDumpNetData: DumpNetData;		setDumpTermData: DumpTermData.	"negotiated state"	doNAWS := false.	cols := rows := 0.! !!TelnetProtocol methodsFor: 'initialize-release'!setDebug: aBoolean	debug := aBoolean! !!TelnetProtocol methodsFor: 'initialize-release'!setDumpNetData: aBoolean	dumpNetData := aBoolean! !!TelnetProtocol methodsFor: 'initialize-release'!setDumpTermData: aBoolean	dumpTermData := aBoolean! !!TelnetProtocol methodsFor: 'commands-do'!iacDo: arg	debug ifTrue: [self rcvd: Do with: arg].	"Assume it's a command we don't want to deal with: refuse it."	self sendIacWont: arg! !!TelnetProtocol methodsFor: 'commands-do'!iacDoEcho: arg	debug ifTrue: [self rcvd: Do with: Echo].	self sendIacWont: Echo! !!TelnetProtocol methodsFor: 'commands-do'!iacDoEnvironmentOpt: arg	debug ifTrue: [self rcvd: Do with: arg].	self sendIacWont: arg! !!TelnetProtocol methodsFor: 'commands-do'!iacDoFlowControl: arg	debug ifTrue: [self rcvd: Do with: arg].! !!TelnetProtocol methodsFor: 'commands-do'!iacDoNAWS: arg	debug ifTrue: [self rcvd: Do with: arg].	doNAWS := true.	(cols ~~ 0 and: [rows ~~ 0])	"previous negotiation delayed"		ifTrue: [self sbWindowSizeSendCols: cols rows: rows]! !!TelnetProtocol methodsFor: 'commands-do'!iacDoNewEnvironment: arg	debug ifTrue: [self rcvd: Do with: arg].	self sendIacWont: arg! !!TelnetProtocol methodsFor: 'commands-do'!iacDoTerminalSpeed: arg	debug ifTrue: [self rcvd: Do with: arg].	self sendIacWont: arg! !!TelnetProtocol methodsFor: 'commands-do'!iacDoTerminalType: arg	debug ifTrue: [self rcvd: Do with: arg].! !!TelnetProtocol methodsFor: 'commands-do'!iacDoXDisplay: arg	debug ifTrue: [self rcvd: Do with: arg].	self sendIacWont: arg! !!TelnetProtocol methodsFor: 'commands-dont'!iacDont: arg	debug ifTrue: [self rcvd: Dont with: arg]! !!TelnetProtocol methodsFor: 'commands-dont'!iacDontNAWS: arg	debug ifTrue: [self rcvd: Dont with: arg].! !!TelnetProtocol methodsFor: 'commands-will'!iacWill: arg	debug ifTrue: [self rcvd: Will with: arg].	self sendIacDont: arg! !!TelnetProtocol methodsFor: 'commands-will'!iacWillEcho: arg	debug ifTrue: [self rcvd: Will with: arg].	self sendIacDo: arg! !!TelnetProtocol methodsFor: 'commands-will'!iacWillSuppressGoAhead: arg	debug ifTrue: [self rcvd: Will with: arg]! !!TelnetProtocol methodsFor: 'commands-subnegotiation'!iacSB: arg	debug ifTrue: [self rcvd: SB with: arg]! !!TelnetProtocol methodsFor: 'commands-subnegotiation'!iacSE: arg	debug ifTrue: [self rcvd: SE with: arg]! !!TelnetProtocol methodsFor: 'commands-other'!iacDataMark: arg	debug ifTrue: [self rcvd: DataMark]! !!TelnetProtocol methodsFor: 'commands-other'!iacIgnored: arg	debug ifTrue: [self rcvd: arg].! !!TelnetProtocol methodsFor: 'commands-outgoing'!sbTerminalTypeSend: arg	debug ifTrue: [self rcvd: SB with: TerminalType sub: Send].	self		sendIacSb: TerminalType		isString: (session propertyAt: #terminalType ifAbsent: ['network'])! !!TelnetProtocol methodsFor: 'commands-outgoing'!sbWindowSizeSendCols: c rows: r	doNAWS		ifTrue: [self sendIacSb: NAWS isShort: c short: r.  cols := rows := 0.]		ifFalse: [cols := c.  rows := r]		"delay for future negotiation"! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIac: arg	down downcallAll: (ByteArray with: IAC with: arg).	debug ifTrue: [self sent: arg]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacDo: arg	down downcallAll: (ByteArray with: IAC with: Do with: arg).	debug ifTrue: [self sent: Do with: arg]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacDont: arg	down downcallAll: (ByteArray with: IAC with: Dont with: arg).	debug ifTrue: [self sent: Dont with: arg]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacSb: arg isShort: s1 short: s2	down downcallAll: (ByteArray streamContents: [:str | str		nextPut: IAC;		nextPut: SB;		nextPut: arg;		nextPut: Is;		nextPut: (s1 bitAnd: 16rFF); nextPut: (s1 bitShift: -8);		nextPut: (s2 bitAnd: 16rFF); nextPut: (s2 bitShift: -8);		nextPut: IAC;		nextPut: SE]).	debug ifTrue: [self sent: SB with: arg sub: Is short: s1 short: s2]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacSb: arg isString: data	down downcallAll: (ByteArray streamContents: [:str | str		nextPut: IAC;		nextPut: SB;		nextPut: arg;		nextPut: Is;		nextPutAll: data asByteArray;		nextPut: IAC;		nextPut: SE]).	debug ifTrue: [self sent: SB with: arg sub: Is string: data]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacWill: arg	down downcallAll: (ByteArray with: IAC with: Will with: arg).	debug ifTrue: [self sent: Will with: arg]! !!TelnetProtocol methodsFor: 'commands-outgoing'!sendIacWont: arg	down downcallAll: (ByteArray with: IAC with: Wont with: arg).	debug ifTrue: [self sent: Wont with: arg]! !!TelnetProtocol methodsFor: 'protocol'!downcall: aChar	dumpTermData ifTrue: [self dump: '['; dumpHex: aChar; dump: ']'].	super downcall: aChar! !!TelnetProtocol methodsFor: 'protocol'!note: aSymbol with: anObject	super note: aSymbol with: anObject.	aSymbol == #windowSize		ifTrue: [^self sbWindowSizeSendCols: anObject x rows: anObject y].	aSymbol == #savePreferences	ifTrue: [^self savePreferences: anObject].	aSymbol == #loadPreferences	ifTrue: [^self loadPreferences: anObject].	aSymbol == #restoreDefaults	ifTrue: [^self restoreDefaults]! !!TelnetProtocol methodsFor: 'protocol'!run	super run.	super sessionNote: #oobInlineEndpoint.	self		sendIacDo: SuppressGoAhead;		sendIacWill: TerminalType;		sendIacWill: NAWS! !!TelnetProtocol methodsFor: 'protocol'!upcall: aChar	dumpNetData ifTrue: [self dump: '<'; dumpHex: aChar; dump: '>'].	super upcall: aChar! !!TelnetProtocol methodsFor: 'debugging'!commandName: cmd	^Commands at: cmd ifAbsent: [cmd printString]! !!TelnetProtocol methodsFor: 'debugging'!cr	self dump: String cr withInternetLineEndings! !!TelnetProtocol methodsFor: 'debugging'!debug: message	self dump: message; dump: String crlf! !!TelnetProtocol methodsFor: 'debugging'!dump: message	message do: [:c | up upcall: c asInteger].	up flush! !!TelnetProtocol methodsFor: 'debugging'!dumpHex: char	| str |	str := char printStringBase: 16.	str := str copyFrom: 4 to: str size.	self dump: (str padded: #left to: 2 with: $0)! !!TelnetProtocol methodsFor: 'debugging'!initWindow	('Connected to ', session endpoint name, '.') withInternetLineEndings asByteArray do: [:c | self upcall: c]! !!TelnetProtocol methodsFor: 'debugging'!optionName: opt	^Options at: opt ifAbsent: [opt printString]! !!TelnetProtocol methodsFor: 'debugging'!rcvd: cmd	self debug: 'RCVD ', (self commandName: cmd)! !!TelnetProtocol methodsFor: 'debugging'!rcvd: cmd with: opt	self debug: 'RCVD ', (self commandName: cmd), ' ', (self optionName: opt)! !!TelnetProtocol methodsFor: 'debugging'!rcvd: cmd with: opt sub: sub	self debug:		'RCVD ', (self commandName: cmd),		' ', (self optionName: opt),		' ', (self subnegotiationName: sub)! !!TelnetProtocol methodsFor: 'debugging'!sent: cmd	self debug: 'SENT ', (self commandName: cmd)! !!TelnetProtocol methodsFor: 'debugging'!sent: cmd with: opt	self debug: 'SENT ', (self commandName: cmd), ' ', (self optionName: opt)! !!TelnetProtocol methodsFor: 'debugging'!sent: cmd with: opt sub: sub short: s1 short: s2	self debug: 'SENT ', (self commandName: cmd),		' ', (self optionName: opt),		' ', (self subnegotiationName: sub),		' ', (s1 bitShift: -8) printString, ' ', (s1 bitAnd: 16rFF) printString, ' (', s1 printString, ')',		' ', (s2 bitShift: -8) printString, ' ', (s2 bitAnd: 16rFF) printString, ' (', s2 printString, ')'! !!TelnetProtocol methodsFor: 'debugging'!sent: cmd with: opt sub: sub string: str	self debug: 'SENT ', (self commandName: cmd),		' ', (self optionName: opt),		' ', (self subnegotiationName: sub),		' "', str, '"'! !!TelnetProtocol methodsFor: 'debugging'!subnegotiationName: sub	^Subnegotiation at: sub ifAbsent: [sub printString]! !!TelnetProtocol methodsFor: 'private'!loadPreferences: dict	debug := dict at: #telnetOptionsDebugging! !!TelnetProtocol methodsFor: 'private'!restoreDefaults	debug := Debug.	dumpNetData := DumpNetData.	dumpTermData := DumpTermData.! !!TelnetProtocol methodsFor: 'private'!savePreferences: dict	dict at: #telnetOptionsDebugging put: debug! !!TelnetProtocol class methodsFor: 'class initialization'!initialize	"TelnetProtocol initialize"	Debug := false.	DumpNetData := false.	DumpTermData := false.	self initializeConstants.	self initializeProtocol.! !!TelnetProtocol class methodsFor: 'class initialization'!initializeConstants	(Commands := IdentityDictionary new)		"	constant name		  value		 diagnostic name	disobfuscational remarks"		at: (SE					:= 240)	put: 'SE';				"Subnegotiation End"		at: (Nop					:= 241)	put: 'NOP';				"No OPeration"		at: (DataMark			:= 242)	put: 'DATA-MARK';		at: (Break				:= 243)	put: 'BREAK';		at: (IP					:= 244)	put: 'IP';				"Interrupt Process"		at: (AO					:= 245)	put: 'AO';				"Abort Output"		at: (AYT					:= 246)	put: 'AYT';				"Are You There?"		at: (EC					:= 247)	put: 'EC';				"Erase Character"		at: (EL					:= 248)	put: 'EL';				"Erase Line"		at: (GoAhead			:= 249)	put: 'GO-AHEAD';		at: (SB					:= 250)	put: 'SB';				"Subnegotiation Begin"		at: (Will					:= 251)	put: 'WILL';		at: (Wont				:= 252)	put: 'WONT';		at: (Do					:= 253)	put: 'DO';		at: (Dont				:= 254)	put: 'DONT';		at: (IAC					:= 255)	put: 'IAC'.				"Interpret As Command"	(Options := IdentityDictionary new)		at: (Echo				:= 1)		put: 'ECHO';		at: (SuppressGoAhead	:= 3)		put: 'SUPPRESS-GO-AHEAD';		at: (Status				:= 5)		put: 'STATUS';		at: (Logout				:= 18)	put: 'LOGOUT';		at: (TerminalType		:= 24)	put: 'TERMINAL-TYPE';		at: (NAWS				:= 31)	put: 'NAWS';		at: (TerminalSpeed		:= 32)	put: 'TERMINAL-SPEED';		at: (RemoteFlowControl	:= 33)	put: 'REMOTE-FLOW-CONTROL';		at: (Linemode			:= 34)	put: 'LINEMODE';		at: (XDisplayLocation	:= 35)	put: 'X-DISPLAY-LOCATION';		at: (EnvironmentOption	:= 36)	put: 'ENVIRONMENT-OPTION';		at: (NewEnvironment	:= 39)	put: 'NEW-ENVIRONMENT';		at: (SuppressLocalEcho	:= 45)	put: 'SUPPRESS-LOCAL-ECHO';		at: (ForwardX			:= 49)	put: 'FORWARD-X'.	(Subnegotiation := IdentityDictionary new)		at: (Is					:= 0)		put: 'IS';		at: (Send				:= 1)		put: 'SEND'.! !!TelnetProtocol class methodsFor: 'class initialization'!initializeProtocol	"TelnetProtocol initialize"	| desc |	desc := StatefulProtocolDescription initialState: #relax.	(desc newState: #relax				-> (#passUp:						-> nil))		add: IAC							-> (nil							-> #iac).	(desc newState: #iac					-> (#iacIgnored:					-> #relax))		add: Do							-> (nil							-> #iacDo);		add: Dont						-> (nil							-> #iacDont);		add: Will						-> (nil							-> #iacWill);		add: SB							-> (nil							-> #iacSB);		add: DataMark					-> (#iacDataMark:				-> #relax)";		add: Nop						-> (#iacNop:						-> #relax);		add: Wont						-> (nil							-> #iacWont);		add: IAC							-> (#iacIAC:						-> #relax)".	(desc newState: #iacDo				-> (#iacDo:						-> #relax))		add: Echo						-> (#iacDoEcho:					-> #relax);		add: TerminalType				-> (#iacDoTerminalType:			-> #relax);		add: NAWS						-> (#iacDoNAWS:					-> #relax);		add: TerminalSpeed				-> (#iacDoTerminalSpeed:			-> #relax);		add: RemoteFlowControl			-> (#iacDoFlowControl:			-> #relax);		add: XDisplayLocation			-> (#iacDoXDisplay:				-> #relax);		add: EnvironmentOption			-> (#iacDoEnvironmentOpt:		-> #relax);		add: NewEnvironment			-> (#iacDoNewEnvironment:		-> #relax).	(desc newState: #iacDont				-> (#iacDont:					-> #relax))		add: NAWS						-> (#iacDontNAWS:				-> #relax).	(desc newState: #iacWill				-> (#iacWill:						-> #relax))			add: Echo						-> (#iacWillEcho:				-> #relax);		add: SuppressGoAhead			-> (#iacWillSuppressGoAhead:	-> #relax).	"xxx THE FOLLOWING SHOULD BE GENERIC WITH SB ARG ACCUMULATOR VIZ TERM EMU xxx"	(desc newState: #iacSB				-> (#iacSB:						-> #relax))		add: TerminalType				-> (nil							-> #sbTermType).	(desc newState: #sbTermType		-> (nil							-> #relax))		add: Send						-> (nil							-> #sbTermTypeSend).	(desc newState: #sbTermTypeSend	-> (nil							-> #relax))		add: IAC							-> (nil							-> #sbTermTypeSendIAC).	(desc newState: #sbTermTypeSendIAC	-> (nil							-> #relax))		add: SE							-> (#sbTerminalTypeSend:		-> #relax).	States := desc compile! !!TelnetProtocol class methodsFor: 'instance creation'!new	"TelnetProtocol new"	^super new initialState: States! !!TelnetProtocol class methodsFor: 'accessing'!debug	^Debug! !!TelnetProtocol class methodsFor: 'accessing'!toggleDebugging	Debug := Debug not! !!TelnetProtocol class methodsFor: 'menu'!buildOptionsMenu	| m |	m := MenuMorph new defaultTarget: self.	self menu: m add: 'show options processing' var: Debug eval: [Debug := Debug not]			help: 'Dispay telnet options negotiation.'.	self menu: m add: 'print network data' var: DumpNetData eval: [DumpNetData := DumpNetData not]			help: 'Dispay raw network data.'.	self menu: m add: 'print terminal data' var: DumpTermData eval: [DumpTermData := DumpTermData not]			help: 'Dispay raw terminal data.'.	^m! !!TelnetProtocol class methodsFor: 'menu'!menu: aMenu add: aString var: aBoolean eval: aBlock help: helpString	aMenu		add: (aBoolean ifTrue: ['<yes>'] ifFalse: ['<no>']), aString		selector: #menuBlock:		argument: aBlock;		balloonTextForLastItem: helpString! !!TelnetProtocol class methodsFor: 'menu'!menuBlock: aBlock	aBlock value! !!VT102Emulator methodsFor: 'initialize-release'!initialState: aState	super initialState: aState.	arguments := nil.	trace := false.	mode := #vt52.	keypadMode := #normal.	cursorState := nil.	g0Map := g1Map := CharsUS.	charMap := g0Map.	keyMap := IdentityDictionary new.	self setCursorKeysNormal! !!VT102Emulator methodsFor: 'initialize-release'!initialize! !!VT102Emulator methodsFor: 'initialize-release'!window: aWindow	window := aWindow.	window setAutoLinefeed: false! !!VT102Emulator methodsFor: 'arguments'!addArgument: arg	arguments		at: arguments size		put: (arguments last * 10 + (arg - $0 asInteger))! !!VT102Emulator methodsFor: 'arguments'!clearArguments: arg	arguments := OrderedCollection with: 0! !!VT102Emulator methodsFor: 'arguments'!newArgument: arg	arguments addLast: 0! !!VT102Emulator methodsFor: 'arguments'!oneArgument: arg	arguments := arg! !!VT102Emulator methodsFor: 'control characters'!bel: arg	"ring keyboard bell"	self traceControl: 'BEL'.	Smalltalk beep! !!VT102Emulator methodsFor: 'control characters'!bs: arg	"backspace"	self traceControl: 'BS'.	mode == #vt52		ifTrue: [window cursorLeft: 1 wrapping: false]		ifFalse: [window bs]! !!VT102Emulator methodsFor: 'control characters'!cr: arg	"carriage return"	self traceControl: 'CR'.	window cr! !!VT102Emulator methodsFor: 'control characters'!ht: arg	"horizontal tab"	self traceControl: 'HT'.	window tab! !!VT102Emulator methodsFor: 'control characters'!lf: arg	"linefeed (also formfeed and vertical tab)"	self traceControl: 'LF'.	window lf! !!VT102Emulator methodsFor: 'control characters'!si: arg	"Shift In: select G0 charset"	charMap := g0Map! !!VT102Emulator methodsFor: 'control characters'!so: arg	"Shift In: select G1 charset"	charMap := g1Map! !!VT102Emulator methodsFor: 'control sequences'!cub: arg	"CUrsor Back"	| n |	n := arguments last max: 1.	self trace: 'CUB ' with: n.	window cursorLeft: n wrapping: false.! !!VT102Emulator methodsFor: 'control sequences'!cud: arg	"CUrsor Down"	| n |	n := arguments last max: 1.	self trace: 'CUD' with: n.	window cursorDown: n scrolling: false! !!VT102Emulator methodsFor: 'control sequences'!cuf: arg 	| n |	n := arguments last max: 1.	self trace: 'CUF ' with: n.	window cursorRight: n wrapping: false.! !!VT102Emulator methodsFor: 'control sequences'!cup: arg	"CUrsor Position"	"Note: this is identical to HVP"	| x y |	arguments size == 2		ifTrue:			[y := arguments first max: 1.			 x := arguments last max: 1]		ifFalse:			[x := y := 1].	self trace: 'CUP ' with: x with: y.	window activePosition: x@y.! !!VT102Emulator methodsFor: 'control sequences'!cuu: arg	"CUrsor Up"	| n |	n := arguments last max: 1.	self trace: 'CUU' with: n.	window cursorUp: n scrolling: false! !!VT102Emulator methodsFor: 'control sequences'!dch: arg	"Delete CHaracter"	| n |	n := (arguments at: 1 ifAbsent: [1]) max: 1.	window deleteForward: n	! !!VT102Emulator methodsFor: 'control sequences'!dl: arg	"Delete Lines"	| param |	param := arguments last max: 1.	self trace: 'DL' with: param.	window deleteLines: param! !!VT102Emulator methodsFor: 'control sequences'!ed: arg	"Erase in Display"	| param |	param := arguments first.	self trace: 'ED' with: param.	param == 1 ifTrue: [^window clearScreenLeft].	param == 2 ifTrue: [^window clearScreen].	window clearScreenRight.! !!VT102Emulator methodsFor: 'control sequences'!el: arg	"Erase in Line"	| param |	param := arguments last.	self trace: 'EL' with: param.	param == 1 ifTrue: [^window clearLineLeft].	param == 2 ifTrue: [^window clearLine].	window clearLineRight! !!VT102Emulator methodsFor: 'control sequences'!hts: arg	"VT52: cursor home	 ANSI: Horizontal Tab Set"	mode == #vt52		ifTrue:			[self trace: '(VT52) HOME'.			 window activePosition: 1@1]		ifFalse:			[self trace: '(ANSI) HTS'.			 window setTab]! !!VT102Emulator methodsFor: 'control sequences'!hvp: arg	"Horizontal and Vertical Position"	"Note: this is identical to CUP"	| x y |	arguments size == 2		ifTrue:			[y := arguments first max: 1.			 x := arguments last max: 1]		ifFalse:			[x := y := 1].	self trace: 'HVP ' with: x with: y.	window activePosition: x@y.! !!VT102Emulator methodsFor: 'control sequences'!il: arg	"Insert Lines"	| param |	param := arguments last max: 1.	self trace: 'IL' with: param.	window insertLines: param! !!VT102Emulator methodsFor: 'control sequences'!ind: arg	"INDex"	mode == #vt52 ifTrue: [^self cul52: arg].	self trace: 'RI'.	window cursorDown: 1 scrolling: true! !!VT102Emulator methodsFor: 'control sequences'!nel: arg	"NExt Line"	self trace: 'NEL'.	window newline! !!VT102Emulator methodsFor: 'control sequences'!ri: arg	"Reverse Index"	self trace: 'RI'.	window cursorUp: 1 scrolling: true! !!VT102Emulator methodsFor: 'control sequences'!rm: arg	"Reset Mode"	| param |	param := arguments at: 1 ifAbsent: [0].	param == 4	ifTrue: [self trace:	'IRM	(insert-replace mode: replace)'. ^window setInsertMode: false].	self debug: 'RM' with: param.! !!VT102Emulator methodsFor: 'control sequences'!scs0: arg	"Select Character Set G0"	| char |	char := Character value: arg.	char == $A	ifTrue: [self trace: 'select G0 UK'.  ^g0Map := CharsUK].	char == $B	ifTrue: [self trace: 'select G0 US'.  ^g0Map := CharsUS].	char == $0	ifTrue: [self trace: 'select G0 GR'.  ^g0Map := CharsGR].	char == $1	ifTrue: [self trace: 'select G0 US'.  ^g0Map := CharsUS].	char == $2	ifTrue: [self trace: 'select G0 US'.  ^g0Map := CharsUS].	self debug: 'SCS G0' withChar: arg! !!VT102Emulator methodsFor: 'control sequences'!scs1: arg	"Select Character Set G1"	| char |	char := Character value: arg.	char == $A	ifTrue: [self trace: 'select G1 UK'.  ^g1Map := CharsUK].	char == $B	ifTrue: [self trace: 'select G1 US'.  ^g1Map := CharsUS].	char == $0	ifTrue: [self trace: 'select G1 GR'.  ^g1Map := CharsGR].	char == $1	ifTrue: [self trace: 'select G1 US'.  ^g1Map := CharsUS].	char == $2	ifTrue: [self trace: 'select G1 US'.  ^g1Map := CharsUS].	self debug: 'SCS G1' withChar: arg! !!VT102Emulator methodsFor: 'control sequences'!sgr: arg	"Select Graphics Rendition"	arguments do: [:gr | self sgrSingle: gr]! !!VT102Emulator methodsFor: 'control sequences'!sgrSingle: arg	self trace: 'SGR' with: arg.	arg == 0 ifTrue: [^window resetVideo].	arg == 1 ifTrue: [^window setEmphasis: 1].	arg == 4 ifTrue: [^window setEmphasis: 4].	arg == 5 ifTrue: [^window setEmphasis: 5].	arg == 7 ifTrue: [^window setEmphasis: 7].		"this is the most common"	"all others are ignored silently"	self debug: 'SGR ', arg printString! !!VT102Emulator methodsFor: 'control sequences'!sm: arg	"Set Mode"	| param |	param := arguments at: 1 ifAbsent: [0].	param == 4	ifTrue: [self trace:	'IRM	(insert-replace mode: insert)'. ^window setInsertMode: true].	self debug: 'SM' with: param.! !!VT102Emulator methodsFor: 'control sequences'!stbm: arg	"Set Top and BottoM lines"	| top bottom |	top := 1.	bottom := window rows.	arguments size == 2		ifTrue:			[top := arguments first max: 1.			 bottom := arguments last min: bottom].	self trace: 'STBM' with: top with: bottom.	top < bottom ifTrue: [window setScrollRegionTop: top bottom: bottom; activePosition: 1@1]! !!VT102Emulator methodsFor: 'control sequences'!tbc: arg	"TaB Clear"	| n |	n := arguments last.	self trace: 'TBC' with: n.	n == 0 ifTrue: [^window clearTab].	n == 3 ifTrue: [^window clearTabs].	self trace: 'TBC' with: n.! !!VT102Emulator methodsFor: 'control sequences-VT52'!ansi52: arg	"enter ANSI mode"	self trace: '(VT52) ANSI'.	self setMode: #ansi! !!VT102Emulator methodsFor: 'control sequences-VT52'!cud52: arg	"VT52: CUrsor Down"	mode == #ansi ifTrue: [^self debug: 'ESC B'].	self trace: '(VT52) CUD'.	window cursorDown: 1 scrolling: false! !!VT102Emulator methodsFor: 'control sequences-VT52'!cul52: arg	"VT52: CUrsor Left"	mode == #ansi ifTrue: [^self debug: 'ESC D'].	self trace: '(VT52) CUL'.	window cursorLeft: 1 wrapping: false! !!VT102Emulator methodsFor: 'control sequences-VT52'!cur52: arg	"VT52: CUrsor Right"	mode == #ansi ifTrue: [^self debug: 'ESC C'].	self trace: '(VT52) CUR'.	window cursorRight: 1 wrapping: false! !!VT102Emulator methodsFor: 'control sequences-VT52'!cuu52: arg	"VT52: CUrsor Up"	mode == #ansi ifTrue: [^self debug: 'ESC A'].	self trace: '(VT52) CUU'.	window cursorUp: 1 scrolling: false! !!VT102Emulator methodsFor: 'control sequences-VT52'!dca52: arg	"VT52: Direct Cursor Adress"	"The manual probably says what to do with unhandled VT52 sequences	 when in ANSI mode but I'm too lazy to wade through it today.  Just barf	 it onto the Transcript for now."	| line col |	mode == #ansi ifTrue: [^self debug: 'ESC Y' withChar: arguments withChar: arg].	line := arguments - 8r037.	col := arg - 8r037.	self trace: '(VT52) DCA' with: col@line.	window activePosition: col@line! !!VT102Emulator methodsFor: 'control sequences-VT52'!ed52: arg	"VT52: Erase to end of Display"	mode == #ansi ifTrue: [^self debug: 'ESC J'].	self trace: '(VT52) ED'.	window clearScreenRight! !!VT102Emulator methodsFor: 'control sequences-VT52'!egm52: arg	"VT52: Enter Graphics Mode"	mode == #ansi ifTrue: [^self debug: 'ESC F'].	self trace: '(VT52) EGM'.	charMap := CharsGR! !!VT102Emulator methodsFor: 'control sequences-VT52'!el52: arg	"VT52: Erase to end of Line"	mode == #ansi ifTrue: [^self debug: 'ESC K'].	self trace: '(VT52) EL'.	window clearLineRight! !!VT102Emulator methodsFor: 'control sequences-VT52'!lgm52: arg	"VT52: Leave Graphics Mode"	mode == #ansi ifTrue: [^self debug: 'ESC G'].	self trace: '(VT52) LGM'.	charMap := CharsUS! !!VT102Emulator methodsFor: 'control sequences-VT52'!rlf52: arg	"VT52: Reverse Line Feed"	mode == #ansi ifTrue: [^self debug: 'ESC I'].	self trace: '(VT52) RLF'.	window cursorUp: 1 scrolling: true! !!VT102Emulator methodsFor: 'control sequences-DEC'!decid: arg	"DEC IDentify terminal (what are you?)"	"Note: the response is the same regardless of the operating mode: we send `ESC / Z'	 which identifies us as a VT102."	self trace: 'DECID -> ESC / Z'.	down		downcall: Character escape asInteger;		downcall: $/ asInteger;		downcall: $Z asInteger! !!VT102Emulator methodsFor: 'control sequences-DEC'!decla: arg	"DEC Line Attributes"	arg == 16r33 ifTrue: [^self debug: 'DECDHL (hi)'].	arg == 16r34 ifTrue: [^self debug: 'DECDHL (lo)'].	arg == 16r35 ifTrue: [^self debug: 'DECSWL'].	arg == 16r36 ifTrue: [^self debug: 'DECDWL'].	arg == 16r38 ifTrue: [self trace: 'DECALN (alignment test)'. ^window fillScreen: $E].	self debug: 'ESC #' withChar: arg.! !!VT102Emulator methodsFor: 'control sequences-DEC'!decpam: arg	"DEC keyPad Application Mode"	self trace: 'DECPNM'.	self flag: #ikp.	"ignored for now -- since I don't have a keypad. ;-)"! !!VT102Emulator methodsFor: 'control sequences-DEC'!decpnm: arg	"DEC keyPad Normal Mode"	self trace: 'DECPNM'.	self flag: #ikp.	"ignored for now -- since I don't have a keypad. ;-)"! !!VT102Emulator methodsFor: 'control sequences-DEC'!decrc: arg	"DEC private Restore Cursor"	cursorState isNil ifFalse:		[window			activePosition:	(cursorState at: 1);			graphicsState:	(cursorState at: 2).		 charMap := cursorState at: 3]! !!VT102Emulator methodsFor: 'control sequences-DEC'!decrm: arg	"DEC private Reset Mode"	arguments do: [:param | self decrmSingle: param]! !!VT102Emulator methodsFor: 'control sequences-DEC'!decrmSingle: param	"DEC private Reset Mode"	param == 1	ifTrue: [self trace:		'DECCKM		(cursor key mode: cursor)'.  ^self setCursorKeysNormal].	param == 2	ifTrue: [self trace:		'DECANM	(ansi mode: VT52)'.  ^self setMode: #vt52].	param == 3	ifTrue: [self trace:		'DECCOLM	(column mode: 80)'. ^window setWidth: 80].	param == 4	ifTrue: [self trace:		'DECSCLM	(scroll mode: jump)'.  ^window setSmoothScroll: false].	param == 5	ifTrue: [self trace:		'DECSCNM	(screen mode: normal)'.  ^window rv: false].	param == 6	ifTrue: [self trace:		'DECOM		(origin mode: absolute)'. ^window setRelativeOrigin: false; activePosition: 1@1].	param == 7	ifTrue: [self trace:		'DECAWM	(autowrap mode: off)'. ^window setAutoWrap: false].	param == 8	ifTrue: [^self trace:		'DECARM	(autorepeat mode: off)'].	param == 18	ifTrue: [^self trace:		'DECPFF		(print FF: off)'].	param == 19	ifTrue: [^self trace:		'DECPEX		(print extent: region)'].	"Emacs somtimes sends this.  Ho hum..."	param == 25	ifTrue: [^window setShowCursor: false].	self debug: 'DECRM' with: param.! !!VT102Emulator methodsFor: 'control sequences-DEC'!decsc: arg	"DEC private Save Cursor"	(cursorState := Array new: 3)		at: 1 put: window activePosition;		at: 2 put: window graphicsState;		at: 3 put: charMap! !!VT102Emulator methodsFor: 'control sequences-DEC'!decsm: arg	"DEC private Set Mode"	arguments do: [:param | self decsmSingle: param]! !!VT102Emulator methodsFor: 'control sequences-DEC' stamp: 'ikp 2/18/2003 23:01'!decsmSingle: param	"DEC private Set Mode"	param == 1	ifTrue: [self trace:		'DECCKM		(cursor key mode: application)'. ^self setCursorKeysApplication].	param == 2	ifTrue: [self trace:		'DECANM	(ansi mode: ANSI)'.  ^self setMode: #ansi].	param == 3	ifTrue: [self trace:		'DECCOLM	(column mode: 132)'. ^window setWidth: 132].	param == 4	ifTrue: [self trace:		'DECSCLM	(scroll mode: smooth)'.  ^window setSmoothScroll: true].	param == 5	ifTrue: [self trace:		'DECSCNM	(screen mode: reverse)'.  ^window rv: true].	param == 6	ifTrue: [self trace:		'DECOM		(origin mode: relative)'. ^window setRelativeOrigin: true; activePosition: 1@1].	param == 7	ifTrue: [self trace:		'DECAWM	(autowrap mode: on)'. ^window setAutoWrap: true].	param == 8	ifTrue: [^self trace:		'DECARM	(autorepeat mode: on)'].	param == 18	ifTrue: [^self trace:		'DECPFF		(print FF: on)'].	param == 19	ifTrue: [^self trace:		'DECPEX		(print extent: screen)'].	"Emacs somtimes sends this.  Ho hum..."	param == 25	ifTrue: [^window setShowCursor: true].	self debug: 'DECSM' with: param.! !!VT102Emulator methodsFor: 'identification'!terminalTypeString	"Viz: RFCs 930 (Telnet Terminal Type Option) and 1700 (Assigned Numbers)"	^'VT102'! !!VT102Emulator methodsFor: 'protocol'!downcall: arg	"Translate Squeak special keys into VT100 control sequences."	(keyMap includesKey: arg)		ifTrue: [down downcallAll: (keyMap at: arg)]		ifFalse: [down downcall: (arg bitAnd: 127)]! !!VT102Emulator methodsFor: 'protocol'!install	super install.	session propertyAt: #terminalType put: self terminalTypeString! !!VT102Emulator methodsFor: 'protocol'!passUp: arg	super passUp: (charMap at: arg + 1)! !!VT102Emulator methodsFor: 'protocol'!run	super run.	window := session		propertyAt: #window		ifAbsent: [^self error: 'Terminal emulators require a window in their protocol stack']! !!VT102Emulator methodsFor: 'private'!csi: arg	self debug: 'CSI' withAll: arguments withChar: arg! !!VT102Emulator methodsFor: 'private'!debug: message	Transcript nextPutAll: '*** '; nextPutAll: message; cr; endEntry! !!VT102Emulator methodsFor: 'private'!debug: message with: arg	Transcript		nextPutAll: '*** ';		nextPutAll: message; space;		print: arg; cr;		endEntry! !!VT102Emulator methodsFor: 'private'!debug: message withAll: args withChar: char	Transcript		nextPutAll: '*** ';		nextPutAll: message; space.	args do: [:a | Transcript print: a; space].	Transcript		print: char asCharacter; space;		nextPut: $(; print: char; nextPut: $); cr;		endEntry.! !!VT102Emulator methodsFor: 'private'!debug: message withChar: char	Transcript		nextPutAll: '*** ';		nextPutAll: message; space;		print: char asCharacter; space;		nextPut: $(; print: char; nextPut: $); cr;		endEntry.! !!VT102Emulator methodsFor: 'private'!debug: message withChar: a withChar: b	Transcript		nextPutAll: '*** ';		nextPutAll: message; space;		print: a asCharacter; space;		print: b asCharacter; space;		nextPut: $(; print: a; space; print: b; nextPut: $); cr;		endEntry.! !!VT102Emulator methodsFor: 'private'!dec: arg	self debug: 'CSI ?' withAll: arguments withChar: arg! !!VT102Emulator methodsFor: 'private'!esc: arg	self debug: 'ESC' withChar: arg! !!VT102Emulator methodsFor: 'private' stamp: 'ikp 2/18/2003 23:04'!setCursorKeysApplication	| esc |	esc := Character escape asString.	keyMap		at: 128+28 put: (esc,'OD') asByteArray;		"left"		at: 128+29 put: (esc,'OC') asByteArray;		"right" 		at: 128+30 put: (esc,'OA') asByteArray;		"up"		at: 128+31 put: (esc,'OB') asByteArray.		"down" ! !!VT102Emulator methodsFor: 'private'!setCursorKeysNormal	| esc |	esc := Character escape asString.	keyMap		at: 128+28 put: (esc,'[D') asByteArray;		"left"		at: 128+29 put: (esc,'[C') asByteArray;		"right" 		at: 128+30 put: (esc,'[A') asByteArray;		"up"		at: 128+31 put: (esc,'[B') asByteArray.		"down" ! !!VT102Emulator methodsFor: 'private'!setMode: aSymbol	mode := aSymbol.! !!VT102Emulator methodsFor: 'private'!trace: message	trace ifTrue: [Transcript nextPutAll: message; cr; endEntry]! !!VT102Emulator methodsFor: 'private'!trace: message with: a	trace ifTrue: [Transcript nextPutAll: message; space; print: a; cr; endEntry]! !!VT102Emulator methodsFor: 'private'!trace: message with: a with: b	trace ifTrue: [Transcript nextPutAll: message; space; print: a; space; print: b; cr; endEntry]! !!VT102Emulator methodsFor: 'private'!traceControl: message	trace ifTrue: [Transcript nextPutAll: message; cr; endEntry]! !!VT102Emulator class methodsFor: 'class initialization'!initialize	"VT102Emulator initialize"	"protocol"	States := self protocol compile.	"character sets"	CharsUK := Array new: 256.	CharsUS := Array new: 256.	CharsGR := Array new: 256.	1 to: 256 do: [:i | CharsUK at: i put: (CharsUS at: i put: (CharsGR at: i put: i - 1))].	1 to: 32 do: [:i | CharsGR at: i + 95 put: (CharsGR at: i)].	CharsUK at: ($# asInteger + 1) put: (CharsGR at: 31)! !!VT102Emulator class methodsFor: 'class initialization'!protocol	| desc |	desc := StatefulProtocolDescription initialState: #relax.	"Default: pass everything up except DEL, ESC and ASCII control chars (see end)"	(desc newState: #relax	-> (#passUp:		-> nil))		add: 8r033			-> (nil			-> #esc);	"ANSI or CSI"		add: 8r177			-> (nil			-> nil).		"ignored on input"	"ESC -- either ANSI control sequence, DEC private or first char of CSI"	(desc newState: #esc		-> (#esc: 				-> #relax))		addInteger: $#		-> (nil					-> #decla);	"line attributes"		addInteger: $<		-> (#ansi52:				-> #relax);	"ansi mode"		addInteger: $>		-> (#decpnm:			-> #relax);	"normal keypad mode"		addInteger: $=		-> (#decpam:			-> #relax);	"application keypad mode"		addInteger: $(		-> (nil					-> #scs0);	"select G0 character set"		addInteger: $)		-> (nil					-> #scs1);	"select G1 character set"		addInteger: $7		-> (#decsc:				-> #relax);	"save cursor"		addInteger: $8		-> (#decrc:				-> #relax);	"restore cursor"		addInteger: $A		-> (#cuu52:				-> #relax);	"VT52: cursor up"		addInteger: $B		-> (#cud52:				-> #relax);	"VT52: cursor down"		addInteger: $C		-> (#cur52:				-> #relax);	"VT52: cursor right"		addInteger: $D		-> (#ind:				-> #relax);	"index (VT52: cursor left)"		addInteger: $E		-> (#nel:				-> #relax);	"next line"		addInteger: $F		-> (#egm52:				-> #relax);	"VT52: enter graphics mode"		addInteger: $G		-> (#lgm52:				-> #relax);	"VT52: leave graphics mode"		addInteger: $H		-> (#hts:				-> #relax);	"horizontal tab set (VT52: home)"		addInteger: $I		-> (#rlf52:				-> #relax);	"VT52: reverse line feed"		addInteger: $J		-> (#ed52:				-> #relax);	"VT52: erase to EOD"		addInteger: $K		-> (#el52:				-> #relax);	"VT52: erase to EOL"		addInteger: $M		-> (#ri:					-> #relax);	"reverse index"		addInteger: $Y		-> (nil					-> #dca1);	"VT52: direct cursor address"		addInteger: $Z		-> (#decid:				-> #relax);	"what are you?"		addInteger: $[		-> (#clearArguments:	-> #csi).		"begin CS"	"ESC # -- dec line attributes"	(desc newState: #decla	-> (#decla:			-> #relax)).	"line attributes"	"ESC ( -- SCS for G0 and G1"	(desc newState: #scs0	-> (#scs0:			-> #relax)).	"select character set for G0"	(desc newState: #scs1	-> (#scs1:			-> #relax)).	"select character set for G1"	"ESC Y -- VT52 cursor addressing"	(desc newState: #dca1	-> (#oneArgument:	-> #dca2)).	"direct cursor address (line)"	(desc newState: #dca2	-> (#dca52:			-> #relax)).	"direct cursor address (col)"	"ESC [ -- CSI"	(desc newState: #csi				-> (#csi:				-> #relax))		addAllInteger: '0123456789'	-> (#addArgument:	-> nil);		addInteger: $;				-> (#newArgument:	-> nil);		addInteger: $?				-> (nil				-> #dec);	"dec private mode"		addInteger: $=				-> (nil				-> #relax);	"alternate keypad mode"		addInteger: $A				-> (#cuu:			-> #relax);	"cursor up"		addInteger: $B				-> (#cud:			-> #relax);	"cursor down"		addInteger: $C				-> (#cuf:			-> #relax);	"cursor forward"		addInteger: $D				-> (#cub:			-> #relax);	"cursor backward"		addInteger: $H				-> (#cup:			-> #relax);	"cursor position"		addInteger: $J				-> (#ed:				-> #relax);	"erase in display"		addInteger: $K				-> (#el:				-> #relax);	"erase in line"		addInteger: $L				-> (#il:				-> #relax);	"insert lines"		addInteger: $M				-> (#dl:				-> #relax);	"delete lines"		addInteger: $P				-> (#dch:			-> #relax);	"delete character"		addInteger: $f				-> (#hvp:			-> #relax);	"horiz and vert pos"		addInteger: $g				-> (#tbc:			-> #relax);	"tab clear"		addInteger: $h				-> (#sm:				-> #relax);	"set mode"		addInteger: $l				-> (#rm:				-> #relax);	"reset mode"		addInteger: $m				-> (#sgr:			-> #relax);	"select graphic rendition"		addInteger: $r				-> (#stbm:			-> #relax).	"set top/bottom"	"CSI ? -- DEC private mode change"	(desc newState: #dec				-> (#dec: 			-> #relax))		addAllInteger: '0123456789'	-> (#addArgument:	-> nil);		addInteger: $;				-> (#newArgument:	-> nil);		addInteger: $h				-> (#decsm:			-> #relax);	"set mode"		addInteger: $l				-> (#decrm:			-> #relax).	"reset mode"	"ASCII control characters are interpreted immediately during ESC, CSI and DEC"	#(esc csi dec relax) do: [:mode | (desc at: mode)		add: 8r000	-> (nil		-> nil);	"NUL: ignored on input"		add: 8r007	-> (#bel:	-> nil);	"Note: this is an OSC delimiter in XTerm"		add: 8r010	-> (#bs:		-> nil);		add: 8r011	-> (#ht:		-> nil);		add: 8r012	-> (#lf:		-> nil);		add: 8r013	-> (#lf:		-> nil);	"VT interpreted as LF"		add: 8r014	-> (#lf:		-> nil);	"FF interpreted as LF"		add: 8r015	-> (#cr:		-> nil);	"FF interpreted as LF"		add: 8r016	-> (#so:		-> nil);	"shift out: select G0 charset"		add: 8r017	-> (#si:		-> nil)].	"shift in: select G1 charset"	"That's it folks.  (Simple when you look at it like this, no? ;-)"	^desc! !!VT102Emulator class methodsFor: 'instance creation'!new	"VT100Emulator new"	^super new initialize initialState: States! !!VT102Emulator class methodsFor: 'accessing'!terminalTypeName	^'vt102'! !!XtermEmulator methodsFor: 'initialize-release'!initialize	super initialize.	savedBuffer := nil! !!XtermEmulator methodsFor: 'identification'!terminalTypeString	"Viz: RFCs 930 (Telnet Terminal Type Option) and 1700 (Assigned Numbers)"	^'XTERM'! !!XtermEmulator methodsFor: 'control sequences'!cha: arg	"Character Position Absolute"	self trace: 'CHA ' , (arguments first max: 1) printString.	window activeColumn: (arguments first max: 1)! !!XtermEmulator methodsFor: 'control sequences'!dch: arg	"Delete Character(s)"	| n |	n := (arguments at: 1 ifAbsent: [1]) max: 1.	window deleteForward: n	! !!XtermEmulator methodsFor: 'control sequences'!decrmSingle: param	self trace: 'DECSM' with: param.	param == 1047 ifTrue: [^self restoreBuffer].	param == 1048 ifTrue: [^self restoreState].	param == 1049 ifTrue: [^self restoreBuffer; restoreState.].	super decrmSingle: param! !!XtermEmulator methodsFor: 'control sequences'!decsmSingle: param	self trace: 'DECSM' with: param.	param == 1047 ifTrue: [^self saveBuffer].	param == 1048 ifTrue: [^self saveState].	param == 1049 ifTrue: [self saveState; saveBuffer. ^window clearScreen; activePosition: 1@1].	super decsmSingle: param! !!XtermEmulator methodsFor: 'control sequences'!ich: arg	"Insert Character(s)"	| n |	n := (arguments at: 1 ifAbsent: [1]) max: 1.	self trace: 'ICH ' , n printString.	window insert: n	! !!XtermEmulator methodsFor: 'control sequences'!il: arg	"Insert Line(s)"	| n |	n := (arguments at: 1 ifAbsent: [1]) max: 1.	window insertLines: n	! !!XtermEmulator methodsFor: 'control sequences'!lpa: arg	"Line Position Absolute"	| y x |	y := 1.	x := window activeColumn.	arguments size > 0 ifTrue: [y := arguments first].	arguments size > 1 ifTrue: [x := arguments at: 2].	window activePosition: x@y! !!XtermEmulator methodsFor: 'control sequences'!osc: arg	"Operating System Command"	| type |	type := arguments first.	(type == 0 or: [type == 1]) ifTrue: [window setIconTitle: arguments last].	(type == 0 or: [type == 2]) ifTrue: [window setWindowTitle: arguments last].	"All others are silently ignored"! !!XtermEmulator methodsFor: 'control sequences'!sgrSingle: arg	self trace: 'SGR' with: arg.	"ANSI colour codes"	arg == 24 ifTrue: [^window setEmphasis: 4 to: 0].	arg == 25 ifTrue: [^window setEmphasis: 5 to: 0].	arg == 27 ifTrue: [^window setEmphasis: 7 to: 0].	(arg >= 30 and: [arg <= 37]) ifTrue: [^window setForeground: arg - 30].	arg == 39 ifTrue: [^window setForeground: 0].	(arg >= 40 and: [arg <= 47]) ifTrue: [^window setBackground: arg - 40].	arg == 49 ifTrue: [^window setBackground: 7].	super sgrSingle: arg! !!XtermEmulator methodsFor: 'arguments'!addStringArgument: arg	arguments		at: arguments size		put: (arguments last , (String with: arg asCharacter))! !!XtermEmulator methodsFor: 'arguments'!newStringArgument: arg	arguments addLast: ''! !!XtermEmulator methodsFor: 'private'!restoreBuffer	"Restore the tty to its previously saved state."	savedBuffer isNil ifFalse: [window bufferState: savedBuffer].	window setEmacsColours: false! !!XtermEmulator methodsFor: 'private'!restoreState	"Pretend we saw Restore Cursor"	^self decrc: nil! !!XtermEmulator methodsFor: 'private'!saveBuffer	"Save a copy of the current state of the tty."	savedBuffer := window bufferState.	window setEmacsColours: true! !!XtermEmulator methodsFor: 'private'!saveState	"Pretend we saw Save Cursor"	^self decsc: nil! !!XtermEmulator methodsFor: 'protocol'!note: aSymbol with: anObject	super note: aSymbol with: anObject.	aSymbol == #endpointClosed ifTrue: [^self restoreBuffer].! !!XtermEmulator class methodsFor: 'class initialization'!initialize	"XtermEmulator initialize"	super initialize.	States := self protocol compile! !!XtermEmulator class methodsFor: 'class initialization'!protocol	"XtermEmulator initialize"	"XTerm is just a glorified VT102."	| desc |	desc := super protocol.	"Vanilla VT102"	"Additional control sequences"	(desc at: #csi)		addInteger:		$@			-> (#ich:					-> #relax);	"insert character"		addInteger:		$d			-> (#lpa:					-> #relax);	"line position abs"		addInteger:		$G			-> (#cha:					-> #relax);	"char position abs"		addInteger:		$L			-> (#il:						-> #relax);	"insert line(s)"		addInteger:		$P			-> (#dch:					-> #relax).	"delete character(s)"	"Operating system commands"	(desc at: #esc)		addInteger:		$]			-> (#clearArguments:		-> #csix).	"Xterm CSI has a numeric prefix delimited by `;' and textual suffix delimited by BEL"	(desc newState:		#csix		-> (nil						-> nil))	"ignore everything except..."		addAllInteger:	'0123456789'	-> (#addArgument:			-> nil);		addInteger:		$;			-> (#newStringArgument:	-> #csix2);		add:				8r007		-> (nil						-> #relax).	"premature BEL"	(desc newState:		#csix2		-> (#addStringArgument:		-> nil))		"eat everything except..."		add:				8r007		-> (#osc:					-> #relax).	"OSC complete"	^desc! !!XtermEmulator class methodsFor: 'instance creation'!new	"XtermEmulator new"	^super new initialState: States! !!XtermEmulator class methodsFor: 'accessing'!terminalTypeName	^'xterm'! !XtermEmulator initialize!VT102Emulator initialize!TelnetProtocol initialize!TeletypeWindow initialize!TeletypeMorph initialize!SimpleTextMorph initialize!SafeSocket initialize!