[GOODIE] 's Tree and TextOrganizer

Stewart MacLean stingray at paradise.net.nz
Tue May 9 01:05:18 UTC 2000


I just noticed that when I received my post the last few lines have disappeared down a black hole! 

*$!# email program.

Here is the TextOrganizer change set again ... duh!


'From Squeak2.8alpha of 6 May 2000 [latest update: #2052] on 7 May 2000 at 9:21:52 pm'!
"Change Set:		Text Organizer 1.0
Date:			7 May 2000
Author:			Stewart MacLean
				stingray at paradise.net.nz

The text organizer application provides a way of arranging chunks of text in a hierarchical fashion.

Enjoy!!
 
Prerequisites:
- ObjectExplorer 
- Object Explorer Fixes And Changes
- Tree

Optional:
- Doug Way's Windoze tree style with + and -

Usage:

To open a fresh organizer evaluate:

TextOrganizer open.

On the hierarchy list pane:

- left click in toggle area expands/contracts current item
- shifted left click in toggle area expands/contracts current item and all its subitems
- right click in toggle area expands/contracts current item and all siblings
- right click on item text gets menu

Menu Options:

add
interpose above
interpose below
delete
delete with parent adopting children
cut
paste below
paste before
paste after
copy
rename
find
find next
sort children
sort children recursively
load
save

Bugs: 

I've been using it for a while, however testing only proves the presence of errors:)
Send any you find to me.

History:

1.0 Initial release"!

NaryTreeNode subclass: #OrganizerNode
	instanceVariableNames: 'name '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tools-Text Organizer'!

!OrganizerNode commentStamp: '<historical>' prior: 0!
A named, n-ary tree node, whose value is String.

Used as the primary domain model for the TextOrganizer.
 !
OrganizerNode class
	instanceVariableNames: ''!
AbstractHierarchicalList subclass: #TextOrganizer
	instanceVariableNames: 'rootObject listMorph findProcess searchString textPaneSelection '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tools-Text Organizer'!

!TextOrganizer commentStamp: 'SIM 5/7/2000 17:38' prior: 0!
Welcome to the Text Organizer.

It provides a way of arranging chunks of text in a hierarchical fashion.

Inspired by VisualWorks WorkspaceOrganizer and made implementable (by me!!) in Morphic by Bob Arnings ObjectExplorer. Thanks Bob.

Enjoy!!
Stewart MacLean, May 2000. e-mail: stingray at paradise.net.nz
 
Prerequisites:
- ObjectExplorer 
- Object Explorer Fixes And Changes
- Tree

Optional:
- Doug Way's Windoze tree style with + and -

Usage:

To open a fresh organizer evaluate:

	TextOrganizer open.

On the hierarchy list pane:

- left click in toggle area expands/contracts current item
- shifted left click in toggle area expands/contracts current item and all its subitems
- right click in toggle area expands/contracts current item and all siblings
- right click on item text gets menu

Menu Options:

add
interpose above
interpose below
delete
delete with parent adopting children
cut
paste below
paste before
paste after
copy
rename
find
find next
sort children
sort children recursively
load
save

Architecture:

TextOrganizer class is the application model of the "text organizer" application.

The text organizer application provides a way of arranging chunks of text 
in a hierarchical fashion. The domain model class is OrganizerNode. For purposes 
of user interaction these are wrappered by TextOrganizerNodeWrapper.

The user interface consists of two panes. The top one is a hierarchical list of headings
(implemented using SimpleHierarchicalListMorph) and the bottom one contains the 
currently selected list item's associated text (implemented using a PluggagleTextMorph).

I was able to leverage Bob's good work on the explorer (I didn't have time to do the hard
bits myself!!). As I wanted the organizer's hierarchical list to behave differently I have had
to separate out the behaviour of SimpleHierarchicalListMorph. I have changed 
SimpleHierarchicalListMorph<<mouseDown: event onItem: aMorph to delegate it's behaviour
to it's model. The main behviour change is to allow for a "very deep recursive" toggle.
This is appropriate for the organizer as the subtrees are bound, whereas with the explorer,
depending on what you were looking at, one could conceivably end up expanding with all
the objects in the image - ouch!!

In order to be able to find all the siblings of any item I found that I had 
to add a parent backpointer to IndentingListItemMorph. 
(The current withSiblingsDo: only includes remaining siblings in the chain).

I also fixed a couple of bugs with the explorer:
- left clicking on an expanded item (in toggle area) where a child is selected, still leaves a highlight when it contracts
- right clicking on an expanded item (in toggle area) causes a walkback

Note that the application model (TextOrganizer) is tightly bound to the list morph, and sends messages directly as well as using the changed: mvc mechanisim.



 

!
TextOrganizer class
	instanceVariableNames: 'pasteBuffer '!
ListItemWrapper subclass: #TextOrganizerNodeWrapper
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tools-Text Organizer'!

!TextOrganizerNodeWrapper commentStamp: '<historical>' prior: 0!
A wrapper of OrganizerNode's, used for interaction.
!
TextOrganizerNodeWrapper class
	instanceVariableNames: ''!
SystemWindow subclass: #TextOrganizerWindow
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Tools-Text Organizer'!

!TextOrganizerWindow commentStamp: '<historical>' prior: 0!
Subclass SystemWindow to overide delete to check if it is ok by the model to close the window.
Current delete sends okToChange, which conflicts with changes in subpanes.!

!OrganizerNode methodsFor: 'initialize-release' stamp: 'SIM 5/7/2000 12:56'!
initialize

	"Initialise the receiver's local variables."

	super initialize.
	name _ 'new kid on the block'.
	value _ String new! !

!OrganizerNode methodsFor: 'finding' stamp: 'SIM 4/26/2000 17:38'!
find: aString for: anApplication

	"Search name and the text of the reciever for occurances of aString.
	If found 'callback' to anApplication with its location.
	Suspend the current active process, which is the find process.
	This enables the search to resume where it left off."

	| index |

	index _ 0.
	[(index _ name
		indexOfSubCollection: aString
		startingAt: index + 1
		ifAbsent: [0]) = 0]
			whileFalse: [
				anApplication foundString: aString withinNameOf: self at: index.
				Processor activeProcess suspend].
	index _ 0.
	[(index _ self text
		indexOfSubCollection: aString
		startingAt: index + 1
		ifAbsent: [0]) = 0]
			whileFalse: [
				anApplication foundString: aString withinValueOf: self at: index.
				Processor activeProcess suspend].
	children do: [: each | each find: aString for: anApplication] 
! !

!OrganizerNode methodsFor: 'accessing' stamp: 'SIM 2/13/2000 14:54'!
name

	"Answer name."

	^name! !

!OrganizerNode methodsFor: 'accessing' stamp: 'SIM 3/20/2000 14:31'!
name: aString

	"Set name to aString."

	name _ aString! !

!OrganizerNode methodsFor: 'accessing' stamp: 'SIM 5/7/2000 12:56'!
name: aNameString text: aTextString

	"Set name to aNameString and contents to aTextString."

	name _ aNameString.
	value _ aTextString! !

!OrganizerNode methodsFor: 'accessing' stamp: 'SIM 5/7/2000 12:56'!
text

	"Answer value, which we treat here as aString"

	^value! !

!OrganizerNode methodsFor: 'accessing' stamp: 'SIM 5/7/2000 12:56'!
text: aString

	"Set value to aString"

	value _ aString! !

!OrganizerNode methodsFor: 'printing' stamp: 'SIM 3/20/2000 14:28'!
printOn: aStream

	"Write a description of the receiver on aStream."

	aStream nextPutAll: name ! !

!OrganizerNode methodsFor: 'structure changes' stamp: 'SIM 4/3/2000 10:28'!
addChildNamed: aString

	"Add a new child to the receiver and assign aString to it's name."

	^(super addChild: self class new)
		name: aString! !

!OrganizerNode methodsFor: 'structure changes' stamp: 'SIM 4/16/2000 16:12'!
interposeNewAboveNamed: aString

	"Add a new child to the receiver's parent and assign aString to it's name.
	The new child adopts the receiver's children."

	^super interposeNewAbove name: aString
! !

!OrganizerNode methodsFor: 'structure changes' stamp: 'SIM 4/16/2000 17:08'!
interposeNewBelowNamed: aString

	"Add a new child to the receiver and assign aString to it's name.
	The new child adopts the receiver's children."

	^super interposeNewBelow name: aString
! !

!OrganizerNode methodsFor: 'structure changes' stamp: 'SIM 5/6/2000 14:45'!
sortChildren

	"Sort the receiver's children."

	children _ (children 
		asSortedCollection: [: a : b | a name <= b name])
			asOrderedCollection! !

!OrganizerNode methodsFor: 'structure changes' stamp: 'SIM 5/6/2000 14:52'!
sortChildrenRecursively

	"Sort the receiver's children, recursively."

	self do: [: node | node sortChildren]! !


!OrganizerNode class methodsFor: 'instance creation' stamp: 'SIM 4/18/2000 10:11'!
name: aString
	
	"Create an OrganiserNode with name aString."

	^self new name: aString! !

!OrganizerNode class methodsFor: 'instance creation' stamp: 'SIM 4/18/2000 10:14'!
name: aNameString text: aTextString
	
	"Create an OrganiserNode with name aTextString."

	^self new name: aNameString text: aTextString! !

!OrganizerNode class methodsFor: 'instance creation' stamp: 'SIM 4/18/2000 09:57'!
new

	"Answer a new initialized instance of the receiver."

	^super new initialize! !

!OrganizerNode class methodsFor: 'instance creation' stamp: 'SIM 4/18/2000 09:51'!
text: aString
	
	"Create an OrganiserNode with text aString."

	^self new text: aString! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM 4/18/2000 10:35'!
buildTestTree: anArray

	"Build a test tree specified by anOrderedCollection where the 
	numbers represent the number of  leaves and the parenthesis 
	specify the rest of the hierarchy.

	OrganizerNode buildTestTree: #((( 2  5)  (3)) (4 (1(1 2))6) (1) (6)) 
	"

	| new |

	new _ self name: 'root'.
	self subTree: anArray 
		of: new 
		label: 'sub'.

	^new! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM 4/18/2000 10:35'!
buildTestTree: anArray maxLabel: anInteger

	"Same as build test tree but adds variable letter length labels at random.
	
	OrganizerNode 
		buildTestTree: #((( 2  5)  (3)) ( 4 (1(1 2)) 6) (1) (6))
		maxLabel: 10 
	"

	| new |

	new _ self name: 'root'.
	self subTree: anArray 
		of: new
		maxLabel: anInteger.

	^new! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM 4/18/2000 10:26'!
getBigTree

	"185 nodes.
	OrganizerNode getBigTree
	"
  
 	^self  	
		buildTestTree: 
			#( 2 (3 (2)) ( 4 (3)) ((1((2((3((2(4 (3)(3)))1))4)1)3))1) ( 4 (3)) 			((1((12(14)(1((2(4))2))1)13))5) ((1(1 (2)) (2) ((1((1((2(4)1)3))5)(3)1))3 4) 1) 5  )
	 	maxLabel: 8! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM 4/18/2000 10:24'!
randomLabel: labelSize

	"Generate and return a random set of letters >= 1 and <= labelSize."

	| lab |

	Seed isNil ifTrue: [Seed _ Random new].
	lab _ String new: ((Seed next * 100) truncated \\ labelSize) + 1.
	1 to: lab size do: [: i | 
		lab 
			at: i 
			put: ((Seed next * 100) truncated \\ 26 + $a asciiValue) asCharacter].
	^lab! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM 4/18/2000 10:35'!
subTree: anArray of: aTreeNode label: aString

	"Recursively analyze the elements of anArray and 
	build standard labels using aString."

	| lab new i |

	i _ 0.
	anArray do: [: e | 	
		(e isKindOf: Array)
			ifTrue: [
				lab _ aString , '.' , (i _ i + 1) printString.
				new _ self name: lab.			
				aTreeNode addChild: new. 
				self subTree: e of: new label: lab]
			ifFalse: [
				1 to: e do: [: r | 
					aTreeNode addChild:
							(self name: aString,'.',(i _ i + 1) printString)]]].! !

!OrganizerNode class methodsFor: 'test tree generation' stamp: 'SIM here ifTrue:
			[self hasEditingConflicts: true.
			^ self changed]
! !


!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/6/2000 16:16'!
addItem

	"Add a new subitem to the currently selected item."

	| name |

	(name _ FillInTheBlankMorph request: 'Enter name of item') isEmpty
		ifTrue: [^self].
	self changed: #expandCurrentSelection.
	currentSelection addChildNamed: name.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 4/26/2000 17:25'!
copyItem

	"Copy the current selection, very deeply."

	self class pasteBuffer: currentSelection veryDeepCopy

	! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 4/21/2000 16:26'!
cutItem

	"Cut the currently selected item.
	Equivalent to deleteWithSubtree."

	self deleteWithSubtree

	! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 4/17/2000 21:39'!
deleteWithParentAdoptingChildren

	"Delete the currently selected item.
	The children of the current item are adopted by its parent."

	currentSelection withoutListWrapper == rootObject
		ifTrue: [^self inform: 'Cant delete root item'].
	currentSelection deleteWithParentAdoptingChildren.	
	currentSelection _ nil. 
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 4/17/2000 21:40'!
deleteWithSubtree

	"Delete the subtree rooted at the currently selected item."

	currentSelection withoutListWrapper == rootObject
		ifTrue: [^self inform: 'Cant delete root item'].
	self class pasteBuffer: currentSelection.
	currentSelection removeFromParent.
	currentSelection _ nil. 
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 4/26/2000 17:25'!
inspectItem

	"Inspect the current item. For debugging."

	currentSelection inspect! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 13:15'!
interposeAbove

	"Add a new subitem between the currently selected item 
	and it's parent."

	| name new |

	self isIllegalRootActionCheck ifTrue: [^self].
	(name _ FillInTheBlankMorph request: 'Enter name of item') isEmpty
		ifTrue: [^self].
	new _ currentSelection interposeNewAboveNamed: name.
	currentSelection _ new.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text;
		changed: #expandCurrentSelection! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/6/2000 16:16'!
interposeBelow

	"Add a new subitem between the currently selected item 
	and it's children."

	| name |

	(name _ FillInTheBlankMorph request: 'Enter name of item') isEmpty
		ifTrue: [^self].
	self changed: #expandCurrentSelection.
	currentSelection _ currentSelection interposeNewBelowNamed: name.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text;
		changed: #recursiveExpandCurrentSelection! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 21:14'!
loadOrganizer

	"Load an organizer tree from disk."

	| name stream file |

	file _ StandardFileMenu oldFileStream.
	file ifNil: [^self].
	name _ file name.

	rootObject _ (stream _ ReferenceStream oldFileNamed: name) next.
	stream close.
	currentSelection _ TextOrganizerNodeWrapper with: rootObject.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text;
		changed: #expandCurrentSelection.
	
! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 13:15'!
pasteItemAfter

	"Add the buffered item after the currently selected item."

	self isIllegalRootActionCheck ifTrue: [^self].
	self class pasteBuffer ifNil: [^self inform: 'Nothing in buffer to paste'].
	self changed: #expandCurrentSelection.
	currentSelection addSiblingAfter: self class pasteBuffer.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 13:15'!
pasteItemBefore

	"Add the buffered item before the currently selected item."

	self isIllegalRootActionCheck ifTrue: [^self].
	self class pasteBuffer ifNil: [^self inform: 'Nothing in buffer to paste'].
	self changed: #expandCurrentSelection.
	currentSelection addSiblingBefore: self class pasteBuffer.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 13:05'!
pasteItemBelow

	"Add the buffered item as a child of the currently selected item."

	self class pasteBuffer ifNil: [^self inform: 'Nothing in buffer to paste'].
	self changed: #expandCurrentSelection.
	currentSelection _ currentSelection addChild: self class pasteBuffer.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/6/2000 16:17'!
rename

	"Rename the currently selected item."

	| name |

	(name := FillInTheBlankMorph 
		request: 'Enter name of item'
		initialAnswer: currentSelection name) isEmpty
			ifTrue: [^self].
	currentSelection name: name.
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/7/2000 17:25'!
saveOrganizer

	"Save the current organizer tree to disk.
	Prompt the user for a file name.
	If the user cancels then answer false,
	otherwise save the organizer to disk and answer true."

	| name |

	(name _ 
		FillInTheBlankMorph 
			request: 'Enter file name of organizer'
			initialAnswer: self defaultFileName) isEmpty
				ifTrue: [^false].

	(ReferenceStream fileNamed: name) 
		nextPut: rootObject;
		close.

	^true
! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/6/2000 14:36'!
sortChildren

	"Sort the children of the current item into alphabetical order."

	currentSelection sortChildren.
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text

	! !

!TextOrganizer methodsFor: 'actions' stamp: 'SIM 5/6/2000 14:49'!
sortChildrenRecursively

	"Sort the receiver's children recursively."

	currentSelection sortChildrenRecursively.
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text

! !

!TextOrganizer methodsFor: 'constants' stamp: 'SIM 4/17/2000 14:06'!
defaultFileName

	^'organizer.dat'! !

!TextOrganizer methodsFor: 'finding' stamp: 'SIM 4/24/2000 20:58'!
findText

	"Prompt the user for the searchString.
	Expand all items.
	Instantiate and fork a process which will suspend each time the searchString
	is found."
	
	findProcess ifNotNil: [findProcess terminate].
	(searchString _ 
		FillInTheBlankMorph 
			request: 'Enter string to find'
			initialAnswer: searchString) isEmpty
				ifTrue: [^self].
	currentSelection _ TextOrganizerNodeWrapper with: rootObject.
	self 	
		changed: #getList;
		changed: #getCurrentSelection;
		changed: #text;
		changed: #recursiveExpandCurrentSelection.
	findProcess _
		[rootObject find: searchString for: self.
		findProcess _ nil.
		self inform: 'No more items to search - end of find'.
		Processor activeProcess terminate] fork! !

!TextOrganizer methodsFor: 'finding' stamp: 'SIM 4/24/2000 20:58'!
findTextNext

	"Find the next occurance of the searchString.
	Implemented by resuming the findProcess."

	findProcess 
		ifNil: [self inform: 'Do find first']
		ifNotNil: [findProcess resume]
		

	! !

!TextOrganizer methodsFor: 'finding' stamp: 'SIM 4/26/2000 17:40'!
foundString: aString withinNameOf: aTreeNode at: aPosition

	" 'Callback' from domain model notifying that aString has been found,
	within aTreeNode's name.
	- deselect currently selected item
	- set the currentSelection and the text positions to be selected
	- update the views"

	currentSelection _ nil. 
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text.
	currentSelection _ TextOrganizerNodeWrapper with: aTreeNode.
	textPaneSelection _ 0 to: 0.
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text! !

!TextOrganizer methodsFor: 'finding' stamp: 'SIM 4/26/2000 17:40'!
foundString: aString withinValueOf: aTreeNode at: aPosition

	" 'Callback' from domain model notifying that aString has been found,
	within the value of aTreeNode.
	- deselect currently selected item
	- set the currentSelection and the text positions to be selected
	- update the views"

	currentSelection _ nil. 
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text.
	currentSelection _ TextOrganizerNodeWrapper with: aTreeNode.
	textPaneSelection _ aPosition to: aPosition + aString size - 1.
	self 
		changed: #getCurrentSelection;
		changed: #getList;
		changed: #text! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 4/4/2000 16:44'!
getList

	"Answer a collection with the wrapped rootObject as it's only member."

	^Array with: 
		(TextOrganizerNodeWrapper with: rootObject)
! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 5/7/2000 16:54'!
hierarchyMenu: aMenu

	"Answer aMenu with the hierarchy pane items added."

	| menu |

	self changed: #modelAccept.
	currentSelection 
		ifNil: [
			menu _ aMenu.
			menu 
				add: 'load' target: self selector: #loadOrganizer] 
		ifNotNil: [
			menu _ aMenu.
			menu
				"add: 'inspect item' target: self selector: #inspectItem;"
				add: 'add' target: self selector: #addItem;
				add: 'interpose above' target: self selector: #interposeAbove;
				add: 'interpose below' target: self selector: #interposeBelow;
				addLine;
				add: 'delete' target: self selector: #deleteWithSubtree;
				add: 'delete with parent adopting children' target: self selector: #deleteWithParentAdoptingChildren;
				addLine;
				add: 'cut' target: self selector: #cutItem;
				add: 'paste below' target: self selector: #pasteItemBelow;
				add: 'paste before' target: self selector: #pasteItemBefore;
				add: 'paste after' target: self selector: #pasteItemAfter;
				add: 'copy' target: self selector: #copyItem;
				addLine;
				add: 'rename' target: self selector: #rename;
				addLine;
				add: 'find' target: self selector: #findText;
				add: 'find next' target: self selector: #findTextNext;
				addLine;
				add: 'sort children' target: self selector: #sortChildren;
				add: 'sort children recursively' target: self selector: #sortChildrenRecursively;
				addLine;
				add: 'load' target: self selector: #loadOrganizer;
				add: 'save' target: self selector: #saveOrganizer].

	^menu! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 4/4/2000 16:40'!
leftButtonPressed: event onItem: aMorph

	"Respond to the leftButtonPressed:onItem: event by
	selecting aMorph on the hierarchical list morph."
	
	listMorph selectMorph: aMorph! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 4/4/2000 16:41'!
leftButtonPressedInToggleArea: event morph: aMorph

	"If the shift button is pressed whilst the left button is pressed
	within the toggle area then recursively toggle the expanded state
	of the selected subtree, otherwise just toggle the expanded state
	of the current item."

	event shiftPressed
		ifFalse: [
			listMorph toggleExpandedStateOf: aMorph]
		ifTrue: [
			listMorph 
				recursiveToggleExpandedState: aMorph]! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 5/6/2000 16:56'!
noteNewSelection: aTextOrganizerNodeWrapper

	"Note the new selection within the hierarchical list morph:
	- propogate a 'model text accept' to the text pane
	- note the new selection
	- update the new selection to the view
	- update the text of the selection to the text pane."

	self changed: #modelAccept.
	currentSelection _ aTextOrganizerNodeWrapper.
	textPaneSelection _ 1 to: 0.
	self changed: #getCurrentSelection;
		changed: #text.
	currentSelection ifNotNil: [
		currentSelection sendSettingMessageTo: self]
	
! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 4/24/2000 21:17'!
rightButtonPressed: event onItem: aMorph

	"Pop up the hierarchy pane menu."

	listMorph yellowButtonActivity: event shiftPressed! !

!TextOrganizer methodsFor: 'hierarchical list pane' stamp: 'SIM 4/24/2000 21:18'!
rightButtonPressedInToggleArea: event morph: aMorph

	"If the shift button is pressed, toggle the expanded state of all of the
	siblings of aMorph."

	event shiftPressed
		ifFalse: [
			listMorph 
				toggleExpandedStateOfSiblingsOf: aMorph;
				selectMorph: aMorph]
		ifTrue: []! !

!TextOrganizer methodsFor: 'initialise-release' stamp: 'SIM 4/24/2000 21:19'!
initialize

	"Initialise the receiver's instance variables."

	searchString _ ''.
	textPaneSelection _ 1 to: 0  "null selection"! !

!TextOrganizer methodsFor: 'open/close' stamp: 'SIM 5/7/2000 17:23'!
okToClose

	"Prompt the user to save the data before closing the window.
	Give them the option to cancel the close."

	(SelectionMenu confirm: 'Save Organizer before closing?'
		orCancel: [^false])
			ifTrue: [^self saveOrganizer].

	^true
	! !

!TextOrganizer methodsFor: 'open/close' stamp: 'SIM 4/25/2000 22:54'!
openOrganizerOn: anObject

	"Open a TextOrganizer on anObject."

    (self organizerOn: anObject) openInWorldExtent: 500 at 600.

    ^ self
! !

!TextOrganizer methodsFor: 'open/close' stamp: 'SIM 4/17/2000 14:36'!
organizerOn: anObject

	"Instantiate and bind the text organizer components and then
	open for user interaction.
	See class comment.'"

	| window |

	rootObject _ anObject.
	(window _ TextOrganizerWindow labelled: 'Organizer')
		model: self;
		color: Color red.
	window addMorph: (listMorph _ SimpleHierarchicalListMorph 
			on: self
			list: #getList
			selected: #getCurrentSelection
			changeSelected: #noteNewSelection:
			menu: #hierarchyMenu:
			keystroke: nil)
		frame: (0 at 0 corner: 1 at 0.66).
	window addMorph: ((PluggableTextMorph on: self text: #text accept: #text:
				readSelection: #contentsSelection menu: #codePaneMenu:shifted:)
					askBeforeDiscardingEdits: true)
		frame: (0 at 0.66 corner: 1 at 1).
	listMorph autoDeselect: false.
     ^ window! !

!TextOrganizer methodsFor: 'private' stamp: 'SIM 5/7/2000 13:17'!
isIllegalRootActionCheck

	"Check if current item is the root.
	If it is inform the user that operation is illegal and answer true.
	Otherwise answer false."	

	^currentSelection withoutListWrapper == rootObject
		ifTrue: [
			self inform: 'Can''t perform this action with root'.
			true]
		ifFalse: [false]! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/4/2000 16:04'!
codePaneMenu: aMenu shifted: shifted

	"Note that unless we override perform:orSendTo:, 
	PluggableTextController will respond to all menu items"

	^StringHolder basicNew 
		codePaneMenu: aMenu shifted: shifted
! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/17/2000 17:35'!
contentsSelection

	"Return the interval of text in the code pane to 
	select when I set the pane's contents"

	^textPaneSelection

	"^1 to: 0"  "null selection"! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/4/2000 16:04'!
doItContext

	"Answer the context in which a text selection can be evaluated."

	^nil! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/4/2000 16:04'!
doItReceiver

	"Answer the object that should be informed of the result of evaluating a
	text selection."

	currentSelection ifNil: [^rootObject].

	^currentSelection withoutListWrapper
! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/4/2000 16:05'!
selectedClass

	"Answer the class of the receiver's current selection"

	^self doItReceiver class
! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 3/1/2000 22:55'!
text

	"What goes in the bottom pane"

	^currentSelection 
		ifNil: ['']
		ifNotNil: [currentSelection text]! !

!TextOrganizer methodsFor: 'text pane' stamp: 'SIM 4/17/2000 10:10'!
text: newText

	"Set the text of the currentSelection to newText."

	currentSelection 
		ifNotNil: [currentSelection text: newText asString].

	^true


	! !


!TextOrganizer class methodsFor: 'pasteBuffer' stamp: 'SIM 4/17/2000 13:54'!
pasteBuffer

	"Answer pasteBuffer."

	^pasteBuffer! !

!TextOrganizer class methodsFor: 'pasteBuffer' stamp: 'SIM 4/17/2000 13:54'!
pasteBuffer: aTreeNode

	"Set pasteBuffer to aTreeNode."

	pasteBuffer _ aTreeNode! !

!TextOrganizer class methodsFor: 'comments' stamp: 'SIM 4/16/2000 20:51'!
about

	"Display the receiver's comment."

	StringHolder new 
		textContents: self comment; 
		openLabel: 'about ', self asString
		! !

!TextOrganizer class methodsFor: 'invoking' stamp: 'SIM 5/7/2000 17:17'!
initialNode

	^OrganizerNode 
		name: 'Rename me or load saved organizer.'
		text: ''
! !

!TextOrganizer class methodsFor: 'invoking' stamp: 'SIM 4/17/2000 14:43'!
open

	"Open a TextOrganizer.
	TextOrganizer open.
	"

	TextOrganizer new openOrganizerOn: self initialNode

	
	! !

!TextOrganizer class methodsFor: 'invoking' stamp: 'SIM 4/18/2000 10:34'!
test

	"Open a TextOrganizer on an arbritary hierachy derived from
	the class hierarchy of class Magnitude.

	or try:

	TextOrganizer new openOrganizerOn: OrganizerNode getBigTree.
	(slow for adds!!)"

	TextOrganizer new openOrganizerOn:
		(OrganizerNode 
			deriveHierarchyFrom: Magnitude 
			parents: #superclass 
			children: #subclasses
			do: [: proto : node | 
				node 
					name: proto printString;
					text: proto printString])

	
	! !

!TextOrganizer class methodsFor: 'instance creation' stamp: 'SIM 4/17/2000 16:08'!
new

	"Answer a new initialized instance of the receiver."

	^super new initialize! !


!TextOrganizerNodeWrapper methodsFor: 'copied explorer methods' stamp: 'RAA 6/21/1999 15:48'!
canBeDragged

	^false! !

!TextOrganizerNodeWrapper methodsFor: 'copied explorer methods' stamp: 'SIM 3/20/2000 16:46'!
contents

	^item children 
		inject: OrderedCollection new
		into: [: answer : child |
			answer 
				add: (
					TextOrganizerNodeWrapper
						with: child);
				yourself].

! !

!TextOrganizerNodeWrapper methodsFor: 'copied explorer methods' stamp: 'SIM 3/6/2000 20:49'!
hasContents

	^self contents isEmpty not
	
! !

!TextOrganizerNodeWrapper methodsFor: 'copied explorer methods' stamp: 'SIM 3/20/2000 17:17'!
item: anObject

	item _ anObject

! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/17/2000 10:45'!
addChild: aTreeNodeWrapper

	"Add a TreeNodeWrapper's item to the reciever's wrapped item. 
	Inturn wrap the newly created item and answer the wrapper."

	item addChild: aTreeNodeWrapper withoutListWrapper.
	^aTreeNodeWrapper



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/4/2000 17:12'!
addChildNamed: aString

	"Add a new child named aString to the reciever's
	warpped item. Inturn wrap the newly created item
	and answer the wrapper."

	^TextOrganizerNodeWrapper
		with: (item addChildNamed: aString)



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/21/2000 16:39'!
addSiblingAfter: aTreeNodeWrapper

	"Add a TreeNodeWrapper's item after the reciever's wrapped item."

	item parent addChild: aTreeNodeWrapper withoutListWrapper after: item.
	^aTreeNodeWrapper



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/21/2000 16:38'!
addSiblingBefore: aTreeNodeWrapper

	"Add a TreeNodeWrapper's item before the reciever's wrapped item."

	item parent addChild: aTreeNodeWrapper withoutListWrapper before: item.
	^aTreeNodeWrapper 



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 5/7/2000 12:50'!
asString
	
	"Answer the wrapped item's name."

	^item name
	
! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/3/2000 11:57'!
children

	"Answer the children of the wrapped item."

	^item children! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/3/2000 11:59'!
deleteWithParentAdoptingChildren

	"Delete the wrapped item from it's parent.
	The parent adopts all of the newly orphaned children." 

	item deleteWithParentAdoptingChildren.
	item _ nil
	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/16/2000 16:09'!
interposeNewAboveNamed: aString 

	"Add a new child named aString to the receiver's
	item's parent. The new child adopts the receiver's
	item's children. "

	^TextOrganizerNodeWrapper
		with: (item interposeNewAboveNamed: aString)



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/16/2000 17:08'!
interposeNewBelowNamed: aString 

	"Add a new child named aString to the receiver's
	item. The new child adopts the receiver's
	item's children. "

	^TextOrganizerNodeWrapper
		with: (item interposeNewBelowNamed: aString)



	

	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/3/2000 12:00'!
name

	"Answer the wrapped item's name."

	^item name! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 5/6/2000 16:04'!
name: aString

	"Set the receiver's name to aString.
	Convert tab's to spaces as tabs don't get displayed using SimpleHierarchicalListMorph."

	| newStream |

	newStream _ WriteStream on: String new.
	aString do: [: char |
		char == Character tab
			ifFalse: [newStream nextPut: char]
			ifTrue: [newStream nextPutAll: '    ']].

	^item name: newStream contents! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/4/2000 17:04'!
parent

	"Answer the parent of the wrapped item."

	^item parent! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/17/2000 10:43'!
removeFromParent

	"Remove the wrapped item (and by association all of it's children)
	from it's parent."

	item removeFromParent
	! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 5/6/2000 14:39'!
sortChildren

	"Sort the children of the wrapped item."

	^item sortChildren! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 5/6/2000 14:49'!
sortChildrenRecursively

	"Sort the children of the wrapped item, recursively."

	^item sortChildrenRecursively! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/3/2000 12:02'!
text

	"Answer the wrapped item's text."

	^item text! !

!TextOrganizerNodeWrapper methodsFor: 'organizer' stamp: 'SIM 4/3/2000 12:03'!
text: aString

	"Set the wrapped item's text to aText."

	^item text: aString! !


!TextOrganizerNodeWrapper class methodsFor: 'as yet unclassified' stamp: 'SIM 3/20/2000 16:44'!
with: anObject

	^self new 
		item: anObject 
		! !


!TextOrganizerWindow methodsFor: 'open/close' stamp: 'SIM 5/7/2000 14:16'!
delete

	"Overide delete to check if it is ok by the model to close the window.
	Current delete sends okToChange, which conflicts with changes in subpanes."

	model okToClose ifFalse: [^false].
	super delete! !


!TextOrganizerWindow reorganize!
('open/close' delete)
!


!TextOrganizerNodeWrapper reorganize!
('copied explorer methods' canBeDragged contents hasContents item:)
('organizer' addChild: addChildNamed: addSiblingAfter: addSiblingBefore: asString children deleteWithParentAdoptingChildren interposeNewAboveNamed: interposeNewBelowNamed: name name: parent removeFromParent sortChildren sortChildrenRecursively text text:)
!


!TextOrganizer class reorganize!
('pasteBuffer' pasteBuffer pasteBuffer:)
('comments' about)
('invoking' initialNode open test)
('instance creation' new)
!


!TextOrganizer reorganize!
('actions' addItem copyItem cutItem deleteWithParentAdoptingChildren deleteWithSubtree inspectItem interposeAbove interposeBelow loadOrganizer pasteItemAfter pasteItemBefore pasteItemBelow rename saveOrganizer sortChildren sortChildrenRecursively)
('constants' defaultFileName)
('finding' findText findTextNext foundString:withinNameOf:at: foundString:withinValueOf:at:)
('hierarchical list pane' getList hierarchyMenu: leftButtonPressed:onItem: leftButtonPressedInToggleArea:morph: noteNewSelection: rightButtonPressed:onItem: rightButtonPressedInToggleArea:morph:)
('initialise-release' initialize)
('open/close' okToClose openOrganizerOn: organizerOn:)
('private' isIllegalRootActionCheck)
('text pane' codePaneMenu:shifted: contentsSelection doItContext doItReceiver selectedClass text text:)
!

"Postscript:"

	TextOrganizer about.

	TextOrganizer open.
!


-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/ms-tnef
Size: 15894 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20000509/b6a015e9/attachment.bin


More information about the Squeak-dev mailing list