'From Cuis 1.0 of 4 September 2009 [latest update: #290] on 22 September 2009 at 3:01:21 pm'!

!FillInTheBlankMorph methodsFor: 'initialization' stamp: 'jmv 9/21/2009 17:58'!
createTextPaneExtent: answerExtent acceptBoolean: acceptBoolean
	"create the textPane"
	| result frame |
	result := PluggableTextMorph
				model: self
				editorClass: TextEditor
				textGetter: #response
				textSetter: #response:
				selectionGetter: #selectionInterval
				menuGetter: #paneMenu:shifted:editorClass:.
	result extent: answerExtent.
	result borderWidth: 1.
	result hasUnacceptedEdits: true.
	result acceptOnCR: acceptBoolean.
	frame := LayoutFrame new.
	frame leftFraction: 0.0;
		 rightFraction: 1.0;
		 topFraction: 0.2;
		 bottomFraction: 0.7.
	result layoutFrame: frame.
	self addMorph: result.
	^ result! !


!ObjectExplorer methodsFor: 'accessing' stamp: 'jmv 9/21/2009 18:00'!
explorerFor: anObject 
	| window listMorph |
	rootObject := anObject.
	window := (SystemWindow labelled: (rootObject printStringLimitedTo: 64)) model: self.
	window addMorph: (listMorph := SimpleHierarchicalListMorph 
						model: self
						listGetter: #getList
						indexGetter: #getCurrentSelection
						indexSetter: #noteNewSelection:
						menuGetter: #genericMenu:
						keystrokeAction: #explorerKey:from:)
		frame: (0 @ 0 corner: 1 @ 0.8).
	window 
		addMorph: ((PluggableTextMorph 
				model: self
				editorClass: self editorClass
				textGetter: #trash
				textSetter: #trash:
				selectionGetter: #contentsSelection
				menuGetter: #paneMenu:shifted:editorClass:) askBeforeDiscardingEdits: false)
		frame: (0 @ 0.8 corner: 1 @ 1).
	listMorph autoDeselect: false.
	^window! !


!PluggableButtonMorph methodsFor: 'initialize-release' stamp: 'jmv 9/21/2009 18:14'!
model: anObject stateGetter: getStateSel action: actionSel labelOrGetter: labelSelOrString menuGetter: menuSel

	self model: anObject.
	getStateSelector _ getStateSel.
	actionSelector _ actionSel.
	getMenuSelector _ menuSel.
	labelSelOrString isSymbol ifTrue: [
		getLabelSelector _ labelSelOrString.
		self update: labelSelOrString ]
	ifFalse: [
		self label: labelSelOrString ]
! !


!PluggableButtonMorph class methodsFor: 'example' stamp: 'jmv 9/21/2009 18:17'!
example
	"PluggableButtonMorph example openInWorld"

	| s1 s2 s3 b1 b2 b3 row |
	s1 _ Switch new.
	s2 _ Switch new turnOn.
	s3 _ Switch new.
	s2 onAction: [s3 turnOff].
	s3 onAction: [s2 turnOff].
	b1 _ (PluggableButtonMorph model: s1 stateGetter: #isOn action: #switch) label: 'S1'.
	b2 _ (PluggableButtonMorph model: s2 stateGetter: #isOn action: #turnOn) label: 'S2'.
	b3 _ (PluggableButtonMorph model: s3 stateGetter: #isOn action: #turnOn) label: 'S3'.

	row _ AlignmentMorph proportional
		addInRow: (Array with: b1 with: b2 with: b3);
		extent: 120@35.
	^ row
! !

!PluggableButtonMorph class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 18:15'!
model: anObject stateGetter: getStateSel action: actionSel

	^ self new
		model: anObject
		stateGetter: getStateSel
		action: actionSel
		labelOrGetter: nil
		menuGetter: nil
! !

!PluggableButtonMorph class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 18:18'!
model: anObject stateGetter: getStateSel action: actionSel labelOrGetter: labelSelOrString

	^ self new
		model: anObject
		stateGetter: getStateSel
		action: actionSel
		labelOrGetter: labelSelOrString
		menuGetter: nil! !

!PluggableButtonMorph class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 18:15'!
model: anObject stateGetter: getStateSel action: actionSel labelOrGetter: labelSelOrString menuGetter: menuSel

	^ self new
		model: anObject
		stateGetter: getStateSel
		action: actionSel
		labelOrGetter: labelSelOrString
		menuGetter: menuSel! !


!PluggableTextMorph methodsFor: 'initialization' stamp: 'jmv 9/21/2009 17:51'!
model: anObject editorClass: anEditorClass textGetter: getTextSel textSetter: setTextSel selectionGetter: getSelectionSel menuGetter: getMenuSel
"
on: anObject editorClass: anEditorClass text: getTextSel accept: setTextSel readSelection: getSelectionSel menu: getMenuSel
"
	editorClass _ anEditorClass.
	self model: anObject.
	getTextSelector _ getTextSel.
	setTextSelector _ setTextSel.
	getSelectionSelector _ getSelectionSel.
	getMenuSelector _ getMenuSel.
	self borderWidth: 1.
	self setText: self getText.
	self setSelection: self getSelection.! !


!PluggableTextMorph class methodsFor: 'as yet unclassified' stamp: 'jmv 9/21/2009 18:11'!
model: anObject editorClass: anEditorClass textGetter: getTextSel textSetter: setTextSel

	^ self
		model: anObject
		editorClass: anEditorClass
		textGetter: getTextSel
		textSetter: setTextSel
		selectionGetter: nil
		menuGetter: nil! !

!PluggableTextMorph class methodsFor: 'as yet unclassified' stamp: 'jmv 9/21/2009 18:00'!
model: anObject editorClass: anEditorClass textGetter: getTextSel textSetter: setTextSel selectionGetter: getSelectionSel menuGetter: getMenuSel
"
on: anObject editorClass: anEditorClass text: getTextSel accept: setTextSel readSelection: getSelectionSel menu: getMenuSel
"

	|styler answer |
	answer _ self new.
	(Preferences syntaxHighlightingAsYouType 
			and: [ anObject respondsTo: #shoutAboutToStyle:]) ifTrue: [
		styler _ SHTextStylerST80 new.
		styler view: answer.
		answer styler: styler ].
	answer
		model: anObject
		editorClass: anEditorClass
		textGetter: getTextSel
		textSetter: setTextSel
		selectionGetter: getSelectionSel
		menuGetter: getMenuSel.
	^answer! !


!ProcessBrowser methodsFor: 'views' stamp: 'jmv 9/21/2009 18:01'!
asPrototypeInWindow
	"Create a pluggable version of me, answer a window"

	| window aTextMorph |
	window _ SystemWindow new model: self.
	window
		addMorph: ((PluggableListMorph
				model: self
				listGetter: #processNameList
				indexGetter: #processListIndex
				indexSetter: #processListIndex:
				menuGetter: #processListMenu:
				keystrokeAction: #processListKey:from:)
				enableDragNDrop: false)
		frame: (0 @ 0 extent: 0.5 @ 0.5).
	window
		addMorph: ((PluggableListMorph
				model: self
				listGetter: #stackNameList
				indexGetter: #stackListIndex
				indexSetter: #stackListIndex:
				menuGetter: #stackListMenu:
				keystrokeAction: #stackListKey:from:)
				enableDragNDrop: false)
		frame: (0.5 @ 0.0 extent: 0.5 @ 0.5).
	aTextMorph _ PluggableTextMorph
				model: self
				editorClass: TextEditor
				textGetter: #selectedMethod
				textSetter: nil
				selectionGetter: nil
				menuGetter: nil.
	window
		addMorph: aTextMorph
		frame: (0 @ 0.5 corner: 1 @ 1).
	window setLabel: 'Process Browser'.
	^ window! !

!ProcessBrowser methodsFor: 'views' stamp: 'jmv 9/21/2009 18:01'!
openAsMorph
	"Create a pluggable version of me, answer a window"
	| window aTextMorph |
	window _ SystemWindow new
				model: self.

	deferredMessageRecipient _ WorldState.
	window
		addMorph: ((PluggableListMorph
				model: self
				listGetter: #processNameList
				indexGetter: #processListIndex
				indexSetter: #processListIndex:
				menuGetter: #processListMenu:
				keystrokeAction: #processListKey:from:)
				enableDragNDrop: false)
		frame: (0 @ 0 extent: 0.5 @ 0.5).
	window
		addMorph: ((PluggableListMorph
				model: self
				listGetter: #stackNameList
				indexGetter: #stackListIndex
				indexSetter: #stackListIndex:
				menuGetter: #stackListMenu:
				keystrokeAction: #stackListKey:from:)
				enableDragNDrop: false)
		frame: (0.5 @ 0.0 extent: 0.5 @ 0.5).
	aTextMorph _ PluggableTextMorph
				model: self
				editorClass: TextEditor
				textGetter: #selectedMethod
				textSetter: nil
				selectionGetter: nil
				menuGetter: nil.
	aTextMorph askBeforeDiscardingEdits: false.
	window
		addMorph: aTextMorph
		frame: (0 @ 0.5 corner: 1 @ 1).
	window setUpdatablePanesFrom: #(#processNameList #stackNameList ).
	(window setLabel: 'Process Browser') openInWorld.
	startedCPUWatcher ifTrue: [ self setUpdateCallbackAfter: 7 ].
	^ window! !


!SimpleHierarchicalListMorph class methodsFor: 'instance creation' stamp: 'jmv 9/22/2009 15:01'!
model: anObject listGetter: getListSel indexGetter: getSelectionSel indexSetter: setSelectionSel menuGetter: getMenuSel
	"Create a 'pluggable' list view on the given model parameterized by the given message selectors."

	^ self new
		model: anObject
		listGetter: getListSel 
		indexGetter: getSelectionSel 
		indexSetter: setSelectionSel 
		menuGetter: getMenuSel
		keystrokeAction: #arrowKey:from:		"default"! !


!SimpleServiceEntry methodsFor: 'performing service' stamp: 'jmv 9/21/2009 18:17'!
buttonToTriggerIn: aFileList 
	"Answer a button that will trigger the receiver service in a file list"

	| aButton |
	aButton := PluggableButtonMorph 
				model: self
				stateGetter: nil
				action: #performServiceFor:.
	aButton arguments: { 
				aFileList}.
	aButton
		color: Color transparent;
		label: self buttonLabel;
		askBeforeChanging: true.
	aButton color: aFileList class windowColor duller paler.
	aButton setBalloonText: self description.
	^aButton! !


!StringHolder methodsFor: 'initialize-release' stamp: 'jmv 9/21/2009 18:03'!
embeddedInMorphicWindowLabeled: labelString wrap: aBoolean
	| window |
	window _ (SystemWindow labelled: labelString) model: self.
	window addMorph: ((PluggableTextMorph 
		model: self 
		editorClass: self editorClass
		textGetter: #contents 
		textSetter: #acceptContents:
		selectionGetter: nil 
		menuGetter: #paneMenu:shifted:editorClass:)
			wrapFlag: aBoolean)
				frame: (0@0 corner: 1@1).
	^ window! !

!StringHolder methodsFor: 'initialize-release' stamp: 'jmv 9/21/2009 18:03'!
openAsMorphEditorClass: anEditorClass label: labelString inWorld: aWorld
	"Workspace new openAsMorphLabel: 'Workspace'"
	| window |
	window _ (SystemWindow labelled: labelString) model: self.

	window addMorph: (PluggableTextMorph 
			model: self 
			editorClass: anEditorClass
			textGetter: #contents 
			textSetter: #acceptContents:
			selectionGetter: nil 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0@0 corner: 1@1).

	window openInWorld: aWorld! !


!CodeHolder methodsFor: 'annotation' stamp: 'jmv 9/21/2009 18:12'!
addOptionalAnnotationsTo: window at: fractions plus: verticalOffset
	"Add an annotation pane to the window if preferences indicate a desire for it, and return the incoming verticalOffset plus the height of the added pane, if any"

	| aTextMorph divider delta |
	self wantsAnnotationPane ifFalse: [^ verticalOffset].
	aTextMorph _ PluggableTextMorph 
		model: self
		editorClass: self editorClass
		textGetter: #annotation 
		textSetter: nil.
	aTextMorph
		askBeforeDiscardingEdits: false;
		borderWidth: 0;
		hideScrollBarsIndefinitely.
	divider _ BorderedSubpaneDividerMorph forBottomEdge.
	Preferences alternativeWindowLook ifTrue:[
		divider extent: 4@4; color: Color transparent; borderColor: #raised; borderWidth: 2.
	].
	delta _ self defaultAnnotationPaneHeight.
	window 
		addMorph: aTextMorph 
		fullFrame: (LayoutFrame 
				fractions: fractions 
				offsets: (0@verticalOffset corner: 0@(verticalOffset + delta - 1))).
	window 
		addMorph: divider
		fullFrame: (LayoutFrame 
				fractions: fractions 
				offsets: (0@(verticalOffset + delta - 1) corner: 0@(verticalOffset + delta))).
	^ verticalOffset + delta! !

!CodeHolder methodsFor: 'construction' stamp: 'jmv 9/21/2009 17:54'!
buildMorphicCodePaneWith: editString
	"Construct the pane that shows the code.
	Respect the Preference for standardCodeFont."

	| codePane |
	codePane := PluggableTextMorph
				model: self
				editorClass: self editorClass
				textGetter: #contents
				textSetter: #contents:notifying:
				selectionGetter: #contentsSelection
				menuGetter: #paneMenu:shifted:editorClass:.
	editString
		ifNotNil: [
			codePane editString: editString.
			codePane hasUnacceptedEdits: true].
	^ codePane! !

!CodeHolder methodsFor: 'controls' stamp: 'jmv 9/21/2009 18:16'!
optionalButtonRow
	"Answer a row of control buttons"

	| row button aLabel buttons widths |
	buttons _ OrderedCollection new.
	widths _ OrderedCollection new.
	Preferences menuButtonInToolPane ifTrue: [
		buttons add: self menuButton.
		widths add: 4 ].
	self optionalButtonTuples do: [ :tuple | 
		widths add: tuple first.
		button _ PluggableButtonMorph 
					model: self
					stateGetter: nil
					action: tuple third.
		aLabel := Preferences abbreviatedBrowserButtons 
			ifTrue: [self abbreviatedWordingFor: tuple third]
			ifFalse: [nil].
		button label: (aLabel ifNil: [tuple second asString]).
		tuple size > 3 ifTrue: [button setBalloonText: tuple fourth].
		tuple size > 4 ifTrue: [button triggerOnMouseDown: tuple fifth].
		buttons add: button ].
	row _ AlignmentMorph proportional.
	row addInRow: buttons widthProportionalTo: widths.
	^row! !

!CodeHolder methodsFor: 'categories & search pane' stamp: 'jmv 9/21/2009 17:54'!
newSearchPane
	"Answer a new search pane for the receiver"

	| aTextMorph |
	aTextMorph _ PluggableTextMorph
		model: self
		editorClass: self editorClass
		textGetter: #lastSearchString 
		textSetter: #lastSearchString:
		selectionGetter: nil 
		menuGetter: nil.
	aTextMorph setProperty: #alwaysAccept toValue: true.
	aTextMorph askBeforeDiscardingEdits: false.
	aTextMorph acceptOnCR: true.
	aTextMorph setBalloonText: 'Type here and hit ENTER, and all methods whose selectors match what you typed will appear in the list pane below.'.
	^ aTextMorph! !


!Browser methodsFor: 'class comment pane' stamp: 'jmv 9/21/2009 17:53'!
buildMorphicCommentPane
	"Construct the pane that shows the class comment.
	Respect the Preference for standardCodeFont."

	| commentPane |
	commentPane := BrowserCommentTextMorph
				model: self
				editorClass: self editorClass
				textGetter: #classCommentText
				textSetter: #classComment:notifying:
				selectionGetter: nil
				menuGetter: #paneMenu:shifted:editorClass:.
	^ commentPane! !

!Browser methodsFor: 'initialize-release' stamp: 'jmv 9/21/2009 18:16'!
buildMorphicSwitches
	| instanceSwitch commentSwitch classSwitch row aColor |
	instanceSwitch := PluggableButtonMorph 
				model: self
				stateGetter: #instanceMessagesIndicated
				action: #indicateInstanceMessages.
	instanceSwitch
		label: 'instance';
		askBeforeChanging: true;
		borderWidth: 0.
	commentSwitch := PluggableButtonMorph 
				model: self
				stateGetter: #classCommentIndicated
				action: #plusButtonHit.
	commentSwitch
		label: '?' asText allBold;
		askBeforeChanging: true;
		setBalloonText: 'class comment';
		borderWidth: 0.
	classSwitch := PluggableButtonMorph 
				model: self
				stateGetter: #classMessagesIndicated
				action: #indicateClassMessages.
	classSwitch
		label: 'class';
		askBeforeChanging: true;
		borderWidth: 0.
	row _ AlignmentMorph proportional.
	row
		addInRow: {instanceSwitch. commentSwitch. classSwitch}
		atFractions: #(0.0 0.5 0.65 1.0).
	aColor := Color colorFrom: self class windowColor.
	row color: aColor duller.	"ensure matching button divider color. (see #paneColor)"
	Preferences alternativeWindowLook ifTrue: [aColor := aColor muchLighter].
	{ 
		instanceSwitch.
		commentSwitch.
		classSwitch} do: 
				[:m | 
				m
					color: aColor;
					onColor: aColor twiceDarker offColor: aColor].
	^row! !


!ChangeList methodsFor: 'menu actions' stamp: 'jmv 9/21/2009 17:53'!
buildMorphicCodePaneWith: editString

	| codePane |

	codePane _ AcceptableCleanTextMorph
		model: self
		editorClass: self editorClass
		textGetter: #contents 
		textSetter: #contents:
		selectionGetter: #contentsSelection 
		menuGetter: #paneMenu:shifted:editorClass:.
	editString ifNotNil: [
		codePane editString: editString.
		codePane hasUnacceptedEdits: true
	].
	^codePane
! !

!ChangeList methodsFor: 'menu actions' stamp: 'jmv 9/21/2009 18:16'!
optionalButtonRow
	"Answer a row of buttons to occur in a tool pane"

	| row button buttons widths |
	buttons _ OrderedCollection new.
	widths _ OrderedCollection new.
	self buttonSpecs do: [ :tuple | 
		widths add: tuple first.
		button _ PluggableButtonMorph 
					model: self
					stateGetter: nil
					action: tuple third.
		button
			label: tuple second asString;
			askBeforeChanging: true.
		buttons add: button.
		button setBalloonText: tuple fourth].
	buttons add: self regularDiffButton.
	widths add: 9.
	self wantsPrettyDiffOption ifTrue: [
		buttons add:  self prettyDiffButton.
		widths add: 16 ].
	row _ AlignmentMorph proportional.
	row addInRow: buttons widthProportionalTo: widths.
	^row! !


!Debugger methodsFor: 'initialize' stamp: 'jmv 9/21/2009 18:16'!
customButtonRow
	"Answer a button pane affording the user one-touch access to certain functions; the pane is given the formal name 'customButtonPane' by which it can be retrieved by code wishing to send messages to widgets residing on the pane"

	| aButton aLabel buttons row |
	buttons _ OrderedCollection new.
	self customButtonSpecs do: [ :tuple | 
		aButton := PluggableButtonMorph 
					model: self
					stateGetter: nil
					action: tuple second.
		(#(#proceed #restart #send #doStep #stepIntoBlock #fullStack #where) 
			includes: tuple second) ifTrue: [aButton askBeforeChanging: true].
		aLabel := Preferences abbreviatedBrowserButtons 
					ifTrue: [self abbreviatedWordingFor: tuple second]
					ifFalse: [nil].
		aButton label: (aLabel ifNil: [tuple first asString]).
		tuple size > 2 ifTrue: [aButton setBalloonText: tuple third].
		buttons add: aButton].
		
	row _ AlignmentMorph proportional.
	row addInProportionalRow: buttons.
	^row! !

!Debugger methodsFor: 'initialize' stamp: 'jmv 9/21/2009 17:56'!
openFullMorphicLabel: aLabelString
	"Open a full morphic debugger with the given label"

	| window aListMorph oldContextStackIndex |
	oldContextStackIndex _ contextStackIndex.
	self expandStack. "Sets contextStackIndex to zero."

	window _ (SystemWindow labelled: aLabelString) model: self.
	aListMorph _ PluggableListMorph
		model: self 
		listGetter: #contextStackList
		indexGetter: #contextStackIndex
		indexSetter: #toggleContextStackIndex:
		menuGetter: #contextStackMenu:shifted:
		keystrokeAction: #contextStackKey:from:.
	aListMorph menuTitleSelector: #messageListSelectorTitle.
	window addMorph: aListMorph
		frame: (0@0 corner: 1@0.25).

	self addLowerPanesTo: window at: (0@0.25 corner: 1@0.8) with: nil.

	window addMorph: (
		PluggableListMorph new
			doubleClickSelector: #inspectSelection;
			model: self receiverInspector
			listGetter: #fieldList
			indexGetter: #selectionIndex 
			indexSetter: #toggleIndex:
			menuGetter: #fieldListMenu: 
			keystrokeAction: #inspectorKey:from:)
		frame: (0@0.8 corner: 0.2@1).
	window addMorph: (PluggableTextMorph 
			model: self receiverInspector
			editorClass: self receiverInspector editorClass
			textGetter: #contents 
			textSetter: #accept:
			selectionGetter: #contentsSelection 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0.2@0.8 corner: 0.5@1).
	window addMorph: (
		PluggableListMorph new
			doubleClickSelector: #inspectSelection;
			model: self contextVariablesInspector 
			listGetter: #fieldList
			indexGetter: #selectionIndex 
			indexSetter: #toggleIndex:
			menuGetter: #fieldListMenu: 
			keystrokeAction: #inspectorKey:from:)
		frame: (0.5@0.8 corner: 0.7@1).
	window addMorph: (PluggableTextMorph 
			model: self contextVariablesInspector
			editorClass: self contextVariablesInspector editorClass
			textGetter: #contents 
			textSetter: #accept:
			selectionGetter: #contentsSelection 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0.7@0.8 corner: 1@1).
	window openInWorld.
	self toggleContextStackIndex: oldContextStackIndex.
	^ window ! !

!Debugger methodsFor: 'context stack menu' stamp: 'jmv 9/21/2009 17:55'!
buildMorphicNotifierLabelled: label message: messageString 
	| notifyPane window contentTop extentToUse |
	self expandStack.
	window := (PreDebugWindow labelled: label) model: self.
	contentTop := 0.2.
	extentToUse := 650 @ 320.	"nice and wide to show plenty of the error msg"
	window addMorph: (self buttonRowForPreDebugWindow: window)
		frame: (0 @ 0 corner: 1 @ contentTop).
	messageString notNil 
		ifFalse: [
			notifyPane := PluggableListMorph 
						model: self
						listGetter: #contextStackList
						indexGetter: #contextStackIndex
						indexSetter: #debugAt:
						menuGetter: nil
						keystrokeAction: nil]
		ifTrue: [
			notifyPane := PluggableTextMorph 
						model: self
						editorClass: self editorClass
						textGetter: nil
						textSetter: nil
						selectionGetter: nil
						menuGetter: #debugProceedMenu:.
			notifyPane
				editString: (self preDebugNotifierContentsFrom: messageString);
				askBeforeDiscardingEdits: false].
	window addMorph: notifyPane frame: (0 @ contentTop corner: 1 @ 1).
	^window openInWorldExtent: extentToUse! !


!FileContentsBrowser methodsFor: 'creation' stamp: 'jmv 9/21/2009 17:57'!
addLowerPanesTo: window at: nominalFractions with: editString

	| verticalOffset column codePane infoPane infoHeight divider |


	column _ AlignmentMorph proportional.

	codePane _ PluggableTextMorph 
		model: self 
		editorClass: self editorClass
		textGetter: #contents 
		textSetter: #contents:notifying:
		selectionGetter: #contentsSelection 
		menuGetter: #paneMenu:shifted:editorClass:.
	infoPane _ PluggableTextMorph 
		model: self 
		editorClass: self editorClass
		textGetter: #infoViewContents 
		textSetter: nil
		selectionGetter: nil 
		menuGetter: nil.
	infoPane askBeforeDiscardingEdits: false.
	verticalOffset _ 0.

">>not with this browser--- at least not yet ---
	innerFractions _ 0@0 corner: 1@0.
	verticalOffset _ self addOptionalAnnotationsTo: row at: innerFractions plus: verticalOffset.
	verticalOffset _ self addOptionalButtonsTo: row  at: innerFractions plus: verticalOffset.
<<<<"

	infoHeight _ 20.
	column 
		addMorph: (codePane borderWidth: 0)
		fullFrame: (
			LayoutFrame 
				fractions: (0@0 corner: 1@1) 
				offsets: (0@verticalOffset corner: 0@infoHeight negated)
		).
	divider _ BorderedSubpaneDividerMorph forTopEdge.
	Preferences alternativeWindowLook ifTrue:[
		divider extent: 4@4; color: Color transparent; borderColor: #raised; borderWidth: 2.
	].
	column 
		addMorph: divider
		fullFrame: (
			LayoutFrame 
				fractions: (0@1 corner: 1@1) 
				offsets: (0@infoHeight negated corner: 0@(1-infoHeight))
		).
	column 
		addMorph: (infoPane borderWidth: 0; hideScrollBarsIndefinitely)
		fullFrame: (
			LayoutFrame 
				fractions: (0@1 corner: 1@1) 
				offsets: (0@(1-infoHeight) corner: 0@0)
		).
	window 
		addMorph: column
		frame: nominalFractions.

	column on: #mouseEnter send: #paneTransition: to: window.
	column on: #mouseLeave send: #paneTransition: to: window! !


!FileList class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 18:12'!
addVolumesAndPatternPanesTo: window at: upperFraction plus: offset forFileList: aFileList 
	| column patternHeight volumeListMorph patternMorph divider dividerDelta |
	column _ AlignmentMorph proportional.
	patternHeight _ 25.
	volumeListMorph _ (PluggableListMorph
				model: aFileList
				listGetter: #volumeList
				indexGetter: #volumeListIndex
				indexSetter: #volumeListIndex:
				menuGetter: #volumeMenu:)
				autoDeselect: false.
	volumeListMorph enableDrag: false; enableDrop: true.
	patternMorph _ PluggableTextMorph
				model: aFileList
				editorClass: aFileList editorClass
				textGetter: #pattern
				textSetter: #pattern:.
	patternMorph acceptOnCR: true.
	patternMorph hideScrollBarsIndefinitely.
	divider _ BorderedSubpaneDividerMorph new.
	dividerDelta _ 0.
	Preferences alternativeWindowLook
		ifTrue: [
			divider
				extent: 4 @ 4;
				color: Color transparent;
				borderColor: #raised;
				borderWidth: 2.
			volumeListMorph borderColor: Color transparent.
			patternMorph borderColor: Color transparent.
			dividerDelta _ 3].
	column
		addMorph: (volumeListMorph autoDeselect: false)
		fullFrame: (LayoutFrame
				fractions: (0 @ 0 corner: 1 @ 1)
				offsets: (0 @ 0 corner: 0 @ patternHeight negated - dividerDelta)).
	column
		addMorph: divider
		fullFrame: (LayoutFrame
				fractions: (0 @ 1 corner: 1 @ 1)
				offsets: (0 @ patternHeight negated - dividerDelta corner: 0 @ patternHeight negated)).
	column
		addMorph: patternMorph
		fullFrame: (LayoutFrame
				fractions: (0 @ 1 corner: 1 @ 1)
				offsets: (0 @ patternHeight negated corner: 0 @ 0)).
	window
		addMorph: column
		fullFrame: (LayoutFrame
				fractions: upperFraction
				offsets: (0 @ offset corner: 0 @ 0)).
	Preferences alternativeWindowLook
		ifTrue: [column borderWidth: 2] ifFalse: [column borderWidth: 0]! !

!FileList class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 17:57'!
openAsMorph
	"Open a morphic view of a FileList on the default directory."

	| dir aFileList window upperFraction offset |
	dir := FileDirectory default.
	aFileList := self new directory: dir.
	window := (SystemWindow labelled: dir pathName) model: aFileList.
	upperFraction := 0.3.
	offset := 0.
	self 
		addVolumesAndPatternPanesTo: window
		at: (0 @ 0 corner: 0.3 @ upperFraction)
		plus: offset
		forFileList: aFileList.
	self 
		addButtonsAndFileListPanesTo: window
		at: (0.3 @ 0 corner: 1.0 @ upperFraction)
		plus: offset
		forFileList: aFileList.
	window addMorph: (PluggableTextMorph 
			model: aFileList
			editorClass: aFileList editorClass
			textGetter: #contents
			textSetter: #put:
			selectionGetter: #contentsSelection
			menuGetter: #fileContentsMenu:shifted:)
		frame: (0 @ 0.3 corner: 1 @ 1).
	^window openInWorld! !

!FileList class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 17:58'!
openMorphOn: aFileStream editString: editString 
	"Open a morphic view of a FileList on the given file."
	| fileModel window fileContentsView |

	fileModel _ FileList new setFileStream: aFileStream.	"closes the stream"
	window _ (SystemWindow labelled: aFileStream fullName) model: fileModel.

	window addMorph: (fileContentsView _ PluggableTextMorph 
			model: fileModel 
			editorClass: fileModel editorClass
			textGetter: #contents 
			textSetter: #put:
			selectionGetter: #contentsSelection 
			menuGetter: #fileContentsMenu:shifted:)
		frame: (0@0 corner: 1@1).
	editString ifNotNil: [fileContentsView editString: editString.
			fileContentsView hasUnacceptedEdits: true].

	^ window! !


!FileList2 methodsFor: 'user interface' stamp: 'jmv 9/21/2009 17:58'!
morphicFileContentsPane

	^PluggableTextMorph 
		model: self 
		editorClass: self editorClass
		textGetter: #contents 
		textSetter: #put:
		selectionGetter: #contentsSelection 
		menuGetter: #fileContentsMenu:shifted:
! !

!FileList2 methodsFor: 'user interface' stamp: 'jmv 9/21/2009 18:12'!
morphicPatternPane

	^PluggableTextMorph 
		model: self 
		editorClass: self editorClass
		textGetter: #pattern 
		textSetter: #pattern:
		
! !


!Inspector class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 17:59'!
openAsMorphOn: anObject withLabel: aLabel
	"(Inspector openAsMorphOn: SystemOrganization) openInMVC"
	| window inspector |
	inspector _ self inspect: anObject.
	window _ (SystemWindow labelled: aLabel) model: inspector.
	window addMorph: (
		PluggableListMorph new
			doubleClickSelector: #inspectSelection;
			model: inspector 
			listGetter: #fieldList
			indexGetter: #selectionIndex
			indexSetter: #toggleIndex:
			menuGetter: ((inspector isMemberOf: DictionaryInspector)
					ifTrue: [#dictionaryMenu:]
					ifFalse: [#fieldListMenu:])
			keystrokeAction: #inspectorKey:from:)
		frame: (0@0 corner: self horizontalDividerProportion @ self verticalDividerProportion).
	window addMorph: (PluggableTextMorph
			model: inspector 
			editorClass: inspector editorClass 
			textGetter: #contents 
			textSetter: #accept:
			selectionGetter: #contentsSelection 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (self horizontalDividerProportion @0 corner: 1@self verticalDividerProportion).
	window addMorph: ((PluggableTextMorph 
			model: inspector 
			editorClass: inspector editorClass
			textGetter: #trash 
			textSetter: #trash:
			selectionGetter: #contentsSelection 
			menuGetter: #paneMenu:shifted:editorClass:)
					askBeforeDiscardingEdits: false)
		frame: (0@self verticalDividerProportion corner: 1@1).
	window setUpdatablePanesFrom: #(fieldList).
	window position: 16@0.  "Room for scroll bar."
	^ window! !


!InspectorBrowser class methodsFor: 'as yet unclassified' stamp: 'jmv 9/21/2009 17:59'!
openAsMorphOn: anObject
	"(InspectorBrowser openAsMorphOn: SystemOrganization) openInMVC"
	| window inspector |
	inspector _ self inspect: anObject.
	window _ (SystemWindow labelled: anObject defaultLabelForInspector)
				model: inspector.

	window addMorph: (PluggableListMorph 
			model: inspector 
			listGetter: #fieldList
			indexGetter: #selectionIndex 
			indexSetter: #toggleIndex: 
			menuGetter: #fieldListMenu:)
		frame: (0@0 corner: 0.3@0.5).
	window addMorph: (PluggableTextMorph 
			model: inspector 
			editorClass: inspector editorClass 
			textGetter: #contents 
			textSetter: #accept:
			selectionGetter: nil 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0.3@0 corner: 1.0@0.5).
	window addMorph: (PluggableListMorph 
			model: inspector 
			listGetter: #msgList
			indexGetter: #msgListIndex 
			indexSetter: #msgListIndex: 
			menuGetter: #msgListMenu:)
		frame: (0@0.5 corner: 0.3@1.0).
	window addMorph: (PluggableTextMorph 
			model: inspector 
			editorClass: inspector editorClass 
			textGetter: #msgText 
			textSetter: #msgAccept:from:
			selectionGetter: nil 
			menuGetter: #msgPaneMenu:shifted:)
		frame: (0.3@0.5 corner: 1.0@1.0).
	
	window setUpdatablePanesFrom: #(fieldList msgList).
	window position: 16@0.  "Room for scroll bar."
	^ window! !


!MessageNames methodsFor: 'initialization' stamp: 'jmv 9/21/2009 18:00'!
inMorphicWindowWithInitialSearchString: initialString
	"Answer a morphic window with the given initial search string, nil if none"

"MessageNames openMessageNames"

	| window selectorListView firstDivider secondDivider horizDivider typeInPane searchButton plugTextMor |
	window _ (SystemWindow labelled: 'Message Names') model: self.
	firstDivider _ 0.07.
	secondDivider _ 0.5.
	horizDivider _ 0.5.
	typeInPane _ AlignmentMorph proportional height: 14.

	plugTextMor _ PluggableTextMorph 
		model: self
		editorClass: self editorClass
		textGetter: #searchString 
		textSetter: #searchString:notifying:
		selectionGetter: nil 
		menuGetter: nil.
	plugTextMor setProperty: #alwaysAccept toValue: true.
	plugTextMor askBeforeDiscardingEdits: false.
	plugTextMor acceptOnCR: true.
	plugTextMor setTextColor: Color brown.
	plugTextMor hideScrollBarsIndefinitely.
	plugTextMor setTextMorphToSelectAllOnMouseEnter.

	searchButton _ SimpleButtonMorph new 
		target: self;
		beTransparent;
		label: 'Search';
		actionSelector: #doSearchFrom:;
		arguments: {plugTextMor}.
	searchButton setBalloonText: 'Type some letters into the pane at right, and then press this Search button (or hit RETURN) and all method selectors that match what you typed will appear in the list pane below.  Click on any one of them, and all the implementors of that selector will be shown in the right-hand pane, and you can view and edit their code without leaving this tool.'.

	typeInPane addInProportionalRow: { searchButton. plugTextMor. }.
	initialString isEmptyOrNil ifFalse:
		[plugTextMor setText: initialString].

	window addMorph: typeInPane frame: (0@0 corner: horizDivider @ firstDivider).

	selectorListView _ PluggableListMorph 
		model: self
		listGetter: #selectorList
		indexGetter: #selectorListIndex
		indexSetter: #selectorListIndex:
		menuGetter: #selectorListMenu:
		keystrokeAction: #selectorListKey:from:.
	selectorListView menuTitleSelector: #selectorListMenuTitle.
	window addMorph: selectorListView frame: (0 @ firstDivider corner: horizDivider @ secondDivider).

	window addMorph: self buildMorphicMessageList frame: (horizDivider @ 0 corner: 1@ secondDivider).

	self 
		addLowerPanesTo: window 
		at: (0 @ secondDivider corner: 1@1) 
		with: nil.

	initialString isEmptyOrNil ifFalse:
		[self searchString: initialString notifying: nil].
	^ window! !


!SelectorBrowser methodsFor: 'as yet unclassified' stamp: 'jmv 9/21/2009 18:02'!
morphicWindow
	"Create a Browser that lets you type part of a selector, shows a list of selectors, shows the classes of the one you chose, and spawns a full browser on it.  Answer the window
	SelectorBrowser new open "

	| window typeInView selectorListView classListView |
	window := SystemWindow new model: self.
	selectorIndex := classListIndex := 0.
	typeInView := PluggableTextMorph 
				model: self
				editorClass: self editorClass
				textGetter: #contents
				textSetter: #contents:notifying:
				selectionGetter: #contentsSelection
				menuGetter: #paneMenu:shifted:editorClass:.
	typeInView acceptOnCR: true.
	typeInView hideScrollBarsIndefinitely.
	window addMorph: typeInView frame: (0 @ 0 corner: 0.5 @ 0.14).
	selectorListView := PluggableListMorph 
				model: self
				listGetter: #messageList
				indexGetter: #messageListIndex
				indexSetter: #messageListIndex:
				menuGetter: #selectorMenu:
				keystrokeAction: #messageListKey:from:.
	selectorListView menuTitleSelector: #selectorMenuTitle.
	window addMorph: selectorListView frame: (0 @ 0.14 corner: 0.5 @ 0.6).
	classListView := PluggableListMorph 
				model: self
				listGetter: #classList
				indexGetter: #classListIndex
				indexSetter: #classListIndex:
				menuGetter: nil
				keystrokeAction: #arrowKey:from:.
	classListView menuTitleSelector: #classListSelectorTitle.
	window addMorph: classListView frame: (0.5 @ 0 corner: 1 @ 0.6).
	window 
		addMorph: ((PluggableTextMorph 
				model: self
				editorClass: self editorClass
				textGetter: #byExample
				textSetter: #byExample:
				selectionGetter: #contentsSelection
				menuGetter: #paneMenu:shifted:editorClass:) askBeforeDiscardingEdits: false)
		frame: (0 @ 0.6 corner: 1 @ 1).
	window setLabel: 'Method Finder'.
	^window! !


!SyntaxError class methodsFor: 'instance creation' stamp: 'jmv 9/21/2009 18:03'!
buildMorphicViewOn: aSyntaxError
	"Answer an Morphic view on the given SyntaxError."
	| window |
	window _ (SystemWindow labelled: 'Syntax Error') model: aSyntaxError.

	window addMorph: (PluggableListMorph 
			model: aSyntaxError 
			listGetter: #list
			indexGetter: #listIndex 
			indexSetter: nil 
			menuGetter: #listMenu:)
		frame: (0@0 corner: 1@0.15).

	window addMorph: (PluggableTextMorph 
			model: aSyntaxError 
			editorClass: aSyntaxError editorClass
			textGetter: #contents
			textSetter: #contents:notifying: 
			selectionGetter: #contentsSelection
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0@0.15 corner: 1@1).

	^ window openInWorldExtent: 380@220! !


!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:12'!
buildDetailsText
	detailsText _ PluggableTextMorph
		model: self
		editorClass: TextEditor
		textGetter: #details
		textSetter: nil.
	detailsText hideScrollBarsIndefinitely.
	^detailsText! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:18'!
buildFilterButton
	| filterButton |
	filterButton := PluggableButtonMorph 
				model: self
				stateGetter: nil
				action: #setFilter
				labelOrGetter: #filterButtonLabel.
	filterButton onColor: self runButtonColor offColor: self runButtonColor.
	^filterButton! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:13'!
buildPassFailText
	passFailText _ PluggableTextMorph
		model: self
		editorClass: TextEditor
		textGetter: #passFail
		textSetter: nil.
	passFailText hideScrollBarsIndefinitely.
	^ passFailText! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:18'!
buildRefreshButton
	| refreshButton |
	refreshButton := PluggableButtonMorph 
				model: self
				stateGetter: #refreshButtonState
				action: #refreshTests
				labelOrGetter: #refreshButtonLabel.
	refreshButton
		color: self runButtonColor.
	refreshButton onColor: self runButtonColor offColor: self runButtonColor.
	^refreshButton! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:19'!
buildRunButton
	| runButton |
	runButton := PluggableButtonMorph 
				model: self
				stateGetter: #runButtonState
				action: #runTests
				labelOrGetter: #runButtonLabel.
	runButton
		color: self runButtonColor.
	runButton onColor: self runButtonColor offColor: self runButtonOffColor.
	^runButton! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:19'!
buildRunOneButton
	| runOneButton |
	runOneButton := PluggableButtonMorph 
				model: self
				stateGetter: #runButtonState
				action: #runOneTest
				labelOrGetter: #runOneButtonLabel.
	runOneButton
		color: self runButtonColor.
	runOneButton onColor: self runButtonColor offColor: self runButtonOffColor.
	^runOneButton! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:19'!
buildStopButton
	| stopButton |
	stopButton := PluggableButtonMorph 
				model: self
				stateGetter: #stopButtonState
				action: #terminateRun
				labelOrGetter: #stopButtonLabel.
	stopButton
		color: self runButtonColor.
	stopButton onColor: self runButtonColor offColor: self runButtonOffColor.
	^stopButton! !

!TestRunner methodsFor: 'interface opening' stamp: 'jmv 9/21/2009 18:17'!
optionalButtonRow
	| row btn buttons widths |

	buttons _ OrderedCollection new.
	widths _ OrderedCollection new.
	self optionalButtonTuples do: [ :tuple | 
		widths add: tuple first.
		btn _ PluggableButtonMorph 
			model: self
			stateGetter: nil
			action: tuple third.
		btn
			label: tuple second.
		buttons add: btn].
	row _ AlignmentMorph proportional.
	row addInRow: buttons widthProportionalTo: widths.
	^row! !


!TranscriptStream methodsFor: 'initialization' stamp: 'jmv 9/21/2009 18:03'!
openAsMorphLabel: labelString 
	"Build a morph viewing this transcriptStream"
	| window |
	window _ (SystemWindow labelled: labelString) model: self.
	window addMorph: (PluggableTextMorph 
			model: self 
			editorClass: self editorClass
			textGetter: nil 
			textSetter: nil
			selectionGetter: nil 
			menuGetter: #paneMenu:shifted:editorClass:)
		frame: (0@0 corner: 1@1).
	^ window! !

PluggableTextMorph class removeSelector: #on:editorClass:text:accept:!
PluggableTextMorph class removeSelector: #on:editorClass:text:accept:readSelection:menu:!
PluggableTextMorph removeSelector: #on:editorClass:text:accept:readSelection:menu:!
PluggableButtonMorph class removeSelector: #on:!
PluggableButtonMorph class removeSelector: #on:getState:action:!
PluggableButtonMorph class removeSelector: #on:getState:action:label:!
PluggableButtonMorph class removeSelector: #on:getState:action:label:menu:!
PluggableButtonMorph removeSelector: #on:getState:action:label:menu:!
