'From Cuis 1.0 of 4 September 2009 [latest update: #290] on 7 September 2009 at 10:27:52 am'!

!Abort commentStamp: '<historical>' prior: 0!
A generic 'requested processing was aborted' exception. Might be used when the handler knows what to do without further information.!


!Text methodsFor: 'converting' stamp: 'jmv 9/3/2009 11:17'!
removeAttributesThat: removalBlock replaceAttributesThat: replaceBlock by: convertBlock signalIfWholeParagraphAttribute: doSignal
	"Enumerate all attributes in the receiver. Remove those passing removalBlock and replace those passing replaceBlock after converting it through convertBlock.
	if doSignal is false, work as usual. if doSignal is true, do not remove any paragraphAttribute, and signal an exception to request for a second pass that will remove them"
	
	| added removed new mustSignal |
	"Deliberately optimized for the no-op default."
	added _ removed _ nil.
	mustSignal _ false.
	runs withStartStopAndValueDo: [ :start :stop :attribs | 
		attribs do: [ :attrib |
			(removalBlock value: attrib) ifTrue: [
				(doSignal and: [ attrib isParagraphAttribute])
					ifTrue: [ mustSignal _ true ]
					ifFalse: [
						removed ifNil: [ removed _ WriteStream on: #()].
						removed nextPut: {start. stop. attrib} ].
			] ifFalse:[
				(replaceBlock value: attrib) ifTrue: [
					(doSignal and: [ attrib isParagraphAttribute])
						ifTrue: [ mustSignal _ true ]
						ifFalse: [
							mustSignal _ mustSignal or: [ doSignal and: [ attrib isParagraphAttribute ]].
							removed ifNil: [ removed _ WriteStream on: #()].
							removed nextPut: {start. stop. attrib}.
							new _ convertBlock value: attrib.
							added ifNil: [ added _ WriteStream on: #()].
							added nextPut: {start. stop. new} ]
				].
			].
		].
	].
	"otherwise do the real work"
	removed ifNotNil: [
		removed contents do: [ :spec |
			self removeAttribute: spec last from: spec first to: spec second ]].
	added ifNotNil: [
		added contents do: [ :spec |
			self addAttribute: spec last from: spec first to: spec second ]].
	mustSignal
		ifTrue: [ Abort signal: 'Operation hould be applied to the whole paragraph' ].! !

!Text methodsFor: 'emphasis' stamp: 'jmv 9/3/2009 10:32'!
removeAttribute: att 
	^ self removeAttribute: att from: 1 to: self size! !


!TextEditor methodsFor: 'private' stamp: 'jmv 9/3/2009 10:35'!
unapplyAttribute: aTextAttribute
	"The user selected aTextAttribute to be removed.
	If there is a selection, unapply the attribute to the selection.
	In any case do not use the attribute for the user input (emphasisHere)
	It really looks like the 2 branches in this method should be closer!! (handling of Undo is different, and likely wrong)"

	| thisSel interval |

	aTextAttribute isParagraphAttribute
		ifTrue: [
			interval _ paragraph text encompassParagraph: self selectionInterval.
			paragraph 
				replaceFrom: interval first 
				to: interval last 
				with: ((paragraph text copyFrom: interval first to: interval last)
						removeAttribute: aTextAttribute).
			paragraph composeAll.
			self recomputeSelection ]
		
		ifFalse: [
			thisSel _ self selection.
			thisSel notEmpty
				ifTrue: [
					self replaceSelectionWith: (thisSel removeAttribute: aTextAttribute).
					paragraph composeAll.
					self recomputeSelection] ].

	emphasisHere _ emphasisHere copyWithout: aTextAttribute! !

!TextEditor methodsFor: 'private' stamp: 'jmv 9/3/2009 11:34'!
unapplyAttributesThat: removalBlock fromWholeParagraph: wholeParagraph
	"The user selected aTextAttribute to be removed.
	If there is a selection, unapply the attribute to the selection.
	In any case do not use the attribute for the user input (emphasisHere)
	It really looks like the 2 branches in this method should be closer!! (handling of Undo is different, and likely wrong)"

	| interval newText secondPass |

	emphasisHere _ emphasisHere reject: removalBlock.
	wholeParagraph
		ifTrue: [
			interval _ paragraph text encompassParagraph: self selectionInterval.
			newText _ paragraph text copyFrom: interval first to: interval last.
			newText
				removeAttributesThat: removalBlock
				replaceAttributesThat: [ :attr | false ]
				by: [ :attr | false ]
				signalIfWholeParagraphAttribute: false.
			paragraph 
				replaceFrom: interval first 
				to: interval last 
				with: newText.
			paragraph composeAll.
			self recomputeSelection ]
		
		ifFalse: [
			newText _ self selection.
			newText notEmpty
				ifTrue: [
					"Do a first pass that will remove only regular attributes (i.e. isParagraphAttribute not)"
					secondPass _ false.
					[
						newText
							removeAttributesThat: removalBlock
							replaceAttributesThat: [ :attr | false ]
							by: [ :attr | false ]
							signalIfWholeParagraphAttribute: true
					] on: Abort do: [ :ex |
						secondPass _ true ].
					self replaceSelectionWith: newText.
					paragraph composeAll.
					self recomputeSelection.
					"Do a second pass, calling this method again, that will remove paragraph attributes from whole paragraphs"
					secondPass ifTrue: [
						self unapplyAttributesThat: [ :attr | attr isParagraphAttribute and: [removalBlock value: attr ]] fromWholeParagraph: true ] ] ]! !

