miw@mucs.UUCP (02/06/87)
This is the fourth Manchester Smalltalk goodie. It provides a mechanism for writing out Forms or bits of the screen in PostScript format. The comment at the head of the file describes the operation in more detail. Mario ------------------------------------------------------------------------ Dept. of Computer Science ARPA: miw%uk.ac.man.cs.ux@cs.ucl.ac.uk The University USENET: mcvax!ukc!man.cs.ux!miw Manchester M13 9PL JANET: miw@uk.ac.man.cs.ux U.K. 061-273 7121 x 5699 ------------------------------------------------------------------------ ---------------------------------------------------------------- "This package enables one to write Forms out in PostScript format. Two mechanism are provided: 1. Ability to write out a Form using aForm psWriteOn: 'aFileName' 2. Ability to dump a section of the display screen. Hit shift-control-d: you will be asked to designate an area of the screen, whether you want the cursor to appear in the screen dump, and what file name to write to. " 'From Smalltalk-80, version 2, of April 1, 1983 on 23 January 1987 at 6:49:57 pm'! !Character class methodsFor: 'accessing untypeable characters'! lf "Answer the Character representing a line feed." ^self value: 10! ! !Form methodsFor: 'fileIn/Out'! dumpHexBytesOn: aFile invert: invert "write out the bitmap data to the file in hex, rounding up to byte boundaries, and complementing the bits if necessary" | count bytesPerLine word byte | count _ 0. bytesPerLine _ width + 7 // 8. height timesRepeat: [(bytesPerLine // 2) timesRepeat: [word _ bits at: (count _ count + 1). invert ifTrue: [word _ word bitXor: -1]. aFile nextPutAll: word printHexWord]. bytesPerLine even "do the last byte" ifFalse: [byte _ (bits at: (count _ count + 1)) bitShift: -8. invert ifTrue: [byte _ byte bitXor: -1]. aFile nextPutAll: byte printHexByte]. aFile lf. Processor yield].! ! !SmallInteger methodsFor: 'printing'! printHexByte "print the receiver as two hex chars" | s | s _ String new: 2. s at: 1 put: (Character digitValue: ((self bitAnd: 16rF0) bitShift: -4)). s at: 2 put: (Character digitValue: (self bitAnd: 16r0F)). ^s! printHexWord "print the receiver as four hex chars" | s | s _ String new: 4. s at: 1 put: (Character digitValue: ((self bitAnd: 16rF000) bitShift: -12)). s at: 2 put: (Character digitValue: ((self bitAnd: 16r0F00) bitShift: -8)). s at: 3 put: (Character digitValue: ((self bitAnd: 16r00F0) bitShift: -4)). s at: 4 put: (Character digitValue: (self bitAnd: 16r000F)). ^s! ! !WriteStream methodsFor: 'character writing'! lf "Append a linefeed character to the receiver." self nextPut: Character lf! ! !Form methodsFor: 'conversion to/from PostScript'! psWriteFileOn: fileName "Saves the receiver on the file fileName in PostScript format" " Form fromUser psWriteOn: 'mypic.ps' " self psWriteFileOn: fileName scaleFactor: 1 "seems a reasonable default"! psWriteFileOn: fileName scaleFactor: scaleFactor "Saves the receiver on the file fileName in PostScript format, mapping one Smalltalk bitmap pixel onto 'scaleFactor' PostScript pixels" | file | file _ FileStream fileNamed: fileName. file text. file nextPutAll: '%!!'. file lf. "write the PostScript prologue for this image on the stream. The transformation matrix is chosen so that the reference point of the image is at bottom left." "There are two sensible choices for the transformation matrix: [width 0 0 height 0 0] (for inclusion in TeX files), and [width 0 0 -height 0 height] (for straight output to a PostScript device)" file nextPutAll: ((width*scaleFactor) printString, ' ', (height*scaleFactor) printString, ' scale'). file lf. "assume a unix file" file nextPutAll: (width printString, ' ' , height printString, ' 1 [', width printString, ' 0 0 ', height negated printString, ' 0 ', height printString, '] {<'). file lf. "now dump the image data" self dumpHexBytesOn: file invert: true. "and end the thing off" file nextPutAll: '>} image'; lf. file nextPutAll: 'showpage'. file lf. file close! psWriteOn: fileName "Saves the receiver on the file fileName in PostScript format" " Form fromUser psWriteOn: 'mypic.ps' " self psWriteOn: fileName scaleFactor: 1 "seems a reasonable default"! ! !InputState methodsFor: 'private'! doScreenDump | activeProcess currentCursor currentPoint area rect filename | activeProcess _ ScheduledControllers activeControllerProcess. (Processor includes: activeProcess) ifTrue: [activeProcess suspend]. currentCursor _ Sensor currentCursor. currentPoint _ Sensor cursorPoint. rect _ Rectangle fromUser. area _ Form fromDisplay: rect. BinaryChoice message: 'OR in cursor?' displayAt: Sensor cursorPoint centered: true ifTrue: [area copyBits: currentCursor boundingBox from: currentCursor at: currentPoint - rect origin clippingBox: area boundingBox rule: Form paint mask: Form black] ifFalse: []. filename _ FillInTheBlank request: 'Enter filename for bitmap' initialAnswer: 'filename.ps'. filename isEmpty ifFalse: [[Transcript show: 'dumping to file ', filename, '...'. area psWriteFileOn: filename. Transcript show: 'done' ; cr] forkAt: Processor userSchedulingPriority]. (Processor includes: activeProcess) ifFalse: [ScheduledControllers activeControllerProcess resume]! keyAt: keyNumber put: value | index mask | index _ keyNumber < 8r200 ifTrue: [KeyboardMap at: keyNumber + 1] ifFalse: [keyNumber]. index < 8r200 ifTrue: "Not a potential special character" [value ~= 0 ifTrue: [(ctrlState ~= 0 and: [index = LetterCKey or: [index = $d asciiValue]]) ifTrue: [index = LetterCKey ifTrue: [lshiftState ~= 0 ifTrue: [self forkEmergencyEvaluatorAt: Processor userInterruptPriority] ifFalse: [[ScheduledControllers interruptName: 'User Interrupt'] fork]] ifFalse: [lshiftState ~= 0 ifTrue: [[self doScreenDump] forkAt: Processor userInterruptPriority]]] ifFalse: [^keyboardQueue nextPut: (KeyboardEvent code: keyNumber meta: metaState)]]] ifFalse: [index = CtrlKey ifTrue: [ctrlState _ value bitShift: 1] ifFalse: [index = LshiftKey ifTrue: [lshiftState _ value] ifFalse: [index = RshiftKey ifTrue: [rshiftState _ value] ifFalse: [index = LockKey ifTrue: [lockState _ value bitShift: 2] ifFalse: [(index >= BitMin and: [index <= BitMax]) ifTrue: [mask _ 1 bitShift: index - BitMin. value = 1 ifTrue: [bitState _ bitState bitOr: mask] ifFalse: [bitState _ bitState bitAnd: -1 - mask]] ifFalse: [value ~= 0 ifTrue: [keyboardQueue nextPut: (KeyboardEvent code: keyNumber meta: metaState)]]]]]]. metaState _ (ctrlState bitOr: (lshiftState bitOr: rshiftState)) bitOr: lockState]! ! !ProcessorScheduler methodsFor: 'accessing'!includes: aProcess "Has aProcess been suspended?" ^(quiescentProcessLists at: aProcess priority) includes: aProcess! ! ---------------------------------------------------------------- -- ------------------------------------------------------------------------ Dept. of Computer Science ARPA: miw%uk.ac.man.cs.ux@cs.ucl.ac.uk The University USENET: mcvax!ukc!man.cs.ux!miw Manchester M13 9PL JANET: miw@uk.ac.man.cs.ux U.K. 061-273 7121 x 5699 ------------------------------------------------------------------------
miw@mucs.UUCP (04/03/87)
Here's another Smalltalk goodie (no 5 in a series). This is rather larger than the previous postings, but has been in use for a number of months so hopefully has been shaken down. It (re-)implements the user interface to Smalltalk scrollbars, (hopefully) making them easier to use (judge for yourselves). It replaces some of the existing code in the system, so make sure you have a backup image before you file this in and check out whether you like it or not. More explanation is given in the introductory comment. The first part is a small package that implements vertical and horizontal arrows (the vertical ones are used when scrolling). After that comes the scrollbar stuff proper. Incidentally, this package suffers from a problem which seems (at least to me) difficult to solve. A number of methods in the system are modified in different ways by everybody, e.g. the methods that define pop-up menus, and in this case, the set of different cursors. The problem occurs when you want to file in more than one goodie---you have to ensure that later ones don't undo the work others. Does anyone have any ideas on how we might get round this problem? Mario ------------------------------------------------------------------------ Dept. of Computer Science ARPA: miw%uk.ac.man.cs.ux@cs.ucl.ac.uk The University USENET: mcvax!ukc!man.cs.ux!miw Manchester M13 9PL JANET: miw@uk.ac.man.cs.ux U.K. 061-273 7121 x 5699 ------------------------------------------------------------------------ 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:04:36 pm'! DisplayObject subclass: #Arrow instanceVariableNames: 'origin length ' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Display Objects'! Arrow comment: 'I am the abstract superclass of arrows. I have two instance variables, origin <aPoint> where the arrow begins, and length <aNumber> the length of the arrows. My subclasses add direction. '! !Arrow methodsFor: 'accessing'! length ^length! origin ^origin! origin: aPoint length: aNumber origin _ aPoint. length _ aNumber! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Arrow class instanceVariableNames: ''! !Arrow class methodsFor: 'instance creation'! origin: aPoint length: aNumber ^self new origin: aPoint length: aNumber! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:04:38 pm'! Arrow subclass: #HorizontalArrow instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Display Objects'! HorizontalArrow comment: 'I am the class of arrows drawn in the x-direction. If my instance''s length is negative, it goes leftwards. '! !HorizontalArrow methodsFor: 'display box access'! computeBoundingBox "this method assumes that a cursor with its hotspot at the centre will be used as the arrow head" ^Rectangle origin: (origin x min: origin x + length) @ (origin y - 8) extent: length abs @ 16! ! !HorizontalArrow methodsFor: 'displaying'! displayOn: aDisplayMedium at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm | lineform arrowHead arrowHeadPoint line originPoint lineLength | length = 0 ifTrue: [^self]. "zero length arrows can't be seen" "this is the form we're going to use to draw the arrow line" lineform _ ((Form extent: 2@2) offset: 0@-1) black. "here we fiddle with the length because of the non-zero line thickness" length positive ifTrue: [originPoint _ origin. lineLength _ length] ifFalse: [originPoint _ origin - (lineform width@0). lineLength _ length + 2]. "this is the form we're going to use as the head of the arrow" arrowHead _ lineLength positive ifTrue: [Cursor rightArrowHead] ifFalse: [Cursor leftArrowHead]. "Calculate where the arrow head should go. This assumes that the base of the arrow is at 8@8" arrowHeadPoint _ originPoint + (((lineLength abs - 8 max: 0) * lineLength sign)@0). "display the parts of the arrow" arrowHead displayOn: aDisplayMedium at: arrowHeadPoint+aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm. line _ Line from: originPoint to: arrowHeadPoint withForm: lineform. line displayOn: aDisplayMedium at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm! ! !HorizontalArrow methodsFor: 'accessing'! offset ^(length min: 0) @ -8! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:04:43 pm'! Arrow subclass: #VerticalArrow instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Graphics-Display Objects'! VerticalArrow comment: 'I am the class of arrows drawn in the y-direction. If my instance''s length is negative, it goes upwards. '! !VerticalArrow methodsFor: 'displaying'! displayOn: aDisplayMedium at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm | lineform arrowHead arrowHeadPoint line originPoint lineLength | length = 0 ifTrue: [^self]. "zero length arrows can't be seen" "this is the form we're going to use to draw the arrow line" lineform _ ((Form extent: 2@2) offset: -1@0) black. "here we fiddle with the length because of the non-zero line thickness" length positive ifTrue: [originPoint _ origin. lineLength _ length] ifFalse: [originPoint _ origin - (0@lineform height). lineLength _ length + 2]. "this is the form we're going to use as the head of the arrow" arrowHead _ lineLength positive ifTrue: [Cursor downArrowHead] ifFalse: [Cursor upArrowHead]. "Calculate where the arrow head should go. This assumes that the base of the arrow is at 8@8" arrowHeadPoint _ originPoint + (0@((lineLength abs - 8 max: 0) * lineLength sign)). "display the parts of the arrow" arrowHead displayOn: aDisplayMedium at: arrowHeadPoint+aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm. line _ Line from: originPoint to: arrowHeadPoint withForm: lineform. line displayOn: aDisplayMedium at: aDisplayPoint clippingBox: clipRectangle rule: ruleInteger mask: aForm! ! !VerticalArrow methodsFor: 'display box access'! computeBoundingBox "this method assumes that a cursor with its hotspot at the centre will be used as the arrow head" ^Rectangle origin: (origin x - 8) @ (origin x min: origin x + length) extent: 16 @ length abs! ! !VerticalArrow methodsFor: 'accessing'! offset ^-8 @(length min: 0)! ! ------------------------------------------------------------------------ "This package implements a new user interface for the scrolling mechanism in ScrollController. Whereas the old user interface divided a scroll bar into three different areas, this package uses different buttons to select the different scrolling functions, and makes the scroll bar area uniform. The left button provides rate-controlled continuous scrolling. Pressing the left button down while over the scroll bar causes an up-down arrow cursor to appear. Moving the cursor upwards causes it to change into an uparrow, and drags the 'visible' box within the scroll bar upwards. The speed at which the view and the 'visible' box scroll is governed by the distance the cursor is moved. Moving it downwards causes scrolling in the opposite direction. Thus, to make a text view scroll slowly upwards, press the left button down while over the scroll bar, and move it a short distance downwards. The middle button provides absolute scrolling. While the middle button is held down over the scroll bar, the 'visible' box will follow it, scrolling the view to the corresponding position. The right button provides a jump scroll. Holding it down over the scroll bar cause an uparrow to appear, connecting the current cursor position to the top of the scroll bar. When the button is released, the line in the view next to the tail of the arrow will jump to the top of the view. Extra points to note: While a button is held down over the scroll bar, that is, while scrolling is taking place, all horizontal movements of the mouse are ignored. This means that you need not look at the scroll bar while scrolling, as the cursor cannot wander off it. Various aspects of the scroll bar can be customised. The width of the scroll bar, the width and color of the visible box, and the sensitivity of the left button continuous scroll can all be changed. See the protocol in ScrollController for initialisation. " 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:35 pm'! !Behavior methodsFor: 'enumerating'! allInstancesAndSubInstancesDo: aBlock "evaluate the block for all instances of this class and its subclasses" self allInstancesDo: aBlock. self allSubInstancesDo: aBlock! ! Cursor addClassVarName: 'DownArrowHeadCursor'! Cursor addClassVarName: 'LeftArrowHeadCursor'! Cursor addClassVarName: 'RightArrowHeadCursor'! Cursor addClassVarName: 'UpArrowHeadCursor'! Cursor addClassVarName: 'UpDownCursor'! !Cursor methodsFor: 'displaying'! show "Make the current cursor shape be the receiver." Sensor currentCursor ~= self ifTrue: [Sensor currentCursor: self]! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:39 pm'! !Cursor class methodsFor: 'class initialization'! initializeOthers "initialize any new, non-standard cursors" UpDownCursor _ (Cursor extent: 16@16 fromArray: #( 2r110000000 2r1111000000 2r11111100000 2r111111110000 2r1111111111000 2r110000000 2r110000000 2r110000000 2r110000000 2r110000000 2r110000000 2r1111111111000 2r111111110000 2r11111100000 2r1111000000 2r110000000) offset: -8@-8). UpArrowHeadCursor _ (Cursor extent: 16@16 fromArray: #( 2r110000000 2r1111000000 2r11111100000 2r111111110000 2r1111111111000 2r110000000 2r110000000 2r110000000 2r0 2r0 2r0 2r0 2r0 2r0 2r0 2r0) offset: -8@-8). DownArrowHeadCursor _ (Cursor extent: 16@16 fromArray: #( 2r0 2r0 2r0 2r0 2r0 2r0 2r0 2r0 2r110000000 2r110000000 2r110000000 2r1111111111000 2r111111110000 2r11111100000 2r1111000000 2r110000000) offset: -8@-8). LeftArrowHeadCursor _ (Cursor extent: 16@16 fromArray: #( 2r0 2r0 2r0 2r100000000000 2r1100000000000 2r11100000000000 2r111100000000000 2r1111111100000000 2r1111111100000000 2r111100000000000 2r11100000000000 2r1100000000000 2r100000000000 2r0 2r0 2r0) offset: -8@-8). RightArrowHeadCursor _ (Cursor extent: 16@16 fromArray: #( 2r0 2r0 2r0 2r10000 2r11000 2r11100 2r11110 2r11111111 2r11111111 2r11110 2r11100 2r11000 2r10000 2r0 2r0 2r0) offset: -8@-8) "Cursor initializeOthers"! !Cursor initializeOthers! !Cursor class methodsFor: 'constants'! leftArrowHead ^LeftArrowHeadCursor! rightArrowHead ^RightArrowHeadCursor! upArrowHead ^UpArrowHeadCursor! downArrowHead ^DownArrowHeadCursor! upDown "Answer the instance of me that is the shape of an up-down double-headed arrow. " ^UpDownCursor! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:43 pm'! !DisplayObject methodsFor: 'displaying-Display'! backgroundAt: location "Answer a Form containing the contents of the Display which will be altered if I am displayed at location. The Form remembers location in its offset" | backgroundOrigin | backgroundOrigin _ location + self offset. ^ (Form fromDisplay: (backgroundOrigin extent: self extent)) offset: backgroundOrigin! moveTo: newLoc restoring: background "Move an image to a new location on the Display, restoring the background without causing flashing. Background must be a Form containing the bits to be restored at the previous location (its offset), and this method will update its bits and offset appropriately" | location saveAll rect1 rect2 bothRects | "This method should be rewritten to use the offset in background directly" (location _ background offset) = (newLoc + self offset) ifTrue: [^background]. background offset: 0@0. rect1 _ location extent: self extent. rect2 _ newLoc + self offset extent: self extent. bothRects _ rect1 merge: rect2. (rect1 intersects: rect2) ifTrue: "When overlap, buffer background for both rectangles" [saveAll _ Form fromDisplay: bothRects. background displayOn: saveAll at: rect1 origin - bothRects origin. "now saveAll is clean background; get new bits for background" background copy: (0@0 extent: self extent) from: rect2 origin - bothRects origin in: saveAll rule: Form over. self displayOn: saveAll at: rect2 origin - bothRects origin - self offset. saveAll displayOn: Display at: bothRects origin] ifFalse: "If no overlap, do the simple thing (bothrects might be too big)" [background displayOn: Display at: location. background fromDisplay: rect2. self displayOn: Display at: newLoc]. ^ background offset: newLoc + self offset! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:47 pm'! !InputSensor methodsFor: 'mouse'! waitForMouseChange "Wait for the button state or the current mouse point to change, then report the mouse point." | position buttons | position _ self cursorPoint. buttons _ self buttons. [(self cursorPoint = position) & (self buttons = buttons)] whileTrue: [Processor yield]. ^self cursorPoint! ! !Magnitude methodsFor: 'comparing'! max: maxVal min: minVal "return self constrained to lie between min and max" ^(self max: maxVal) min: minVal! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:48 pm'! !OpaqueForm methodsFor: 'accessing'! offset: aPoint figure offset: aPoint! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:50 pm'! !Point methodsFor: 'comparing'! max: maxPoint min: minPoint "Answer self, constrained to lie between minPoint and maxPoint" ^Point x: (x max: minPoint x min: maxPoint x) y: (y max: minPoint y min: maxPoint y)! ! !Point methodsFor: 'arithmetic'! negated ^x negated @ y negated! ! 'From Smalltalk-80, version 2, of April 1, 1983 on 2 October 1986 at 5:24:51 pm'! ScrollController addClassVarName: 'InsideColorMask'! ScrollController addClassVarName: 'MarkerRegionWidth'! ScrollController addClassVarName: 'RegionWidth'! ScrollController addClassVarName: 'ScrollSpeed'! ScrollController comment: 'I represent control for scrolling using a scrollBar. I am a MouseMenuController that creates a scrollBar, rather than menus. My subclasses add the button menus. I keep control as long as the cursor is inside the view or the scrollBar area. A scrollBar is a rectangular area representing the length of the information being viewed. It contains an inner rectangle whose top y-coordinate represents the relative position of the information visible on the screen with respect to all of the information, and whose size represents the relative amount of that information visible on the screen. The user controls which part of the information is visible by pressing the red button. If the user drags the scroll bar up or down (using the red button), the view scrolls up or down accordingly. The greater the drag, the faster the view scrolls. The yellow button positions the view absolutely. The blue button jumps the line nearest the cursor to the top of the view. Instance Variables: scrollBar <Quadrangle> inside white, the outer rectangle marker <Quadrangle> inside color is InsideColorMask, the inner rectangle savedArea <Form> the area the scrollBar overlaps, restored whenever the scrollBar is hidden Class Variables: InsideColorMask <Form> indicating the color of the inner rectangle RegionWidth <Integer> indicating the width of the outer rectangle MarkerregionWidth <Integer> indicating the width of the inner rectangle '! !ScrollController methodsFor: 'arrow access/display'! displayArrowAt: aPoint "make an arrow based on the position of aPoint relative to the scroll bar, save the background behind it, display the arrow, and return the background." | arrow background | arrow _ self makeArrowFromPoint: aPoint. background _ arrow backgroundAt: aPoint. arrow displayAt: aPoint. ^background! makeArrowFromPoint: aPoint "make an arrow marker of the appropriate length (difference in y between aPoint and the top of the marker, and display it." | length arrow form opaqueArrow | length _ scrollBar inside top - aPoint y. "now make the arrow, and thence an OpaqueForm from it:" arrow _ VerticalArrow origin: 0@0 length: length. "paint the arrow onto a Form" form _ (Form new extent: arrow extent) offset: arrow offset. arrow displayOn: form at: arrow offset negated. "make the Form into an OpaqueForm" opaqueArrow _ OpaqueForm shape: form. ^opaqueArrow! ! !ScrollController methodsFor: 'initialize-release'! initialize super initialize. scrollBar _ Quadrangle new. scrollBar borderWidthLeft: 2 right: 0 top: 2 bottom: 2. marker _ Quadrangle new. marker insideColor: InsideColorMask! ! !ScrollController methodsFor: 'basic control sequence'! controlInitialize "The scrollbar has a two-pixel border, and for alignment it assumes that this sub-view has a one-pixel border and shares another one-pixel border from its neighbor/super view" super controlInitialize. scrollBar region: (0 @ 0 extent: RegionWidth @ (view displayBox height + 2)). marker region: self computeMarkerRegion. scrollBar _ scrollBar align: scrollBar topRight with: view displayBox topLeft - (0@1). marker _ marker align: marker topCenter with: scrollBar inside topCenter. savedArea _ Form fromDisplay: scrollBar. scrollBar displayOn: Display. self moveMarker! ! !ScrollController methodsFor: 'scrolling'! canScrollInDirection: plusOrMinus "answer whether the receiver can scroll in the direction indicated by plusOrMinus: up if <0, down if >0" ^plusOrMinus strictlyPositive ifTrue: [marker top > scrollBar inside top] ifFalse: [marker bottom < scrollBar inside bottom]! scroll "Check to see whether the user wishes to jump, scroll up, or scroll down. " Cursor upDown showWhile: [[self scrollBarContainsCursor] whileTrue: [Processor yield. self scrollAsNecessary]]! scrollAbsolute | scrollingStartLocation cursorPoint oldCursorPt xAxis | scrollingStartLocation _ cursorPoint _ sensor cursorPoint. xAxis _ marker center x. "we constrain the cursor to fall on this xAxis, and within the scroll bar" Cursor cursorLink: false. Cursor marker showWhile: [ [sensor yellowButtonPressed] whileTrue: [ cursorPoint ~= oldCursorPt ifTrue: [oldCursorPt _ cursorPoint. sensor cursorPoint: xAxis @ (cursorPoint y max: scrollBar inside top min: scrollBar inside bottom). self scrollAbsoluteFrom: scrollingStartLocation to: cursorPoint]. cursorPoint _ sensor cursorPoint "could use sensor waitForMouseChange on really fast systems" ] ]. scrollBar display. self moveMarker. Cursor cursorLink: true. "ensure the cursor doesn't leap out of the scroll bar" sensor cursorPoint: xAxis @ cursorPoint y! scrollAbsoluteFrom: scrollingStartLocation to: aPoint | oldMarker | oldMarker _ marker. marker _ marker translateBy: 0 @ ((aPoint y - marker center y) max: (scrollBar inside top - marker top) min: (scrollBar inside bottom - marker bottom)). (oldMarker areasOutside: marker) , (marker areasOutside: oldMarker) do: [:region | Display fill: region rule: Form reverse mask: Form gray]. self scrollView! scrollAmountFrom: aPoint relativeTo: startPoint "answer the number of bits of y-coordinate to scroll relative to aPoint" ^((aPoint - startPoint) y) quo: ScrollSpeed! scrollAsNecessary "scroll relative if the red button is used, absolute if the yellow button is used" sensor redButtonPressed ifTrue: [self scrollRelative]. sensor yellowButtonPressed ifTrue: [self scrollAbsolute]. sensor blueButtonPressed ifTrue: [self scrollJumpUp]! scrollFrom: aPoint relativeTo: startPoint "scroll the view by an amount dictated by the difference between aPoint and startPoint" | scrollAmount | scrollAmount _ self scrollAmountFrom: aPoint relativeTo: startPoint. scrollAmount ~= 0 ifTrue: [ (self canScrollInDirection: scrollAmount) ifTrue: [ self scrollView: scrollAmount. self moveMarker]]! scrollRelative | scrollingStartLocation cursorPoint oldCursorPt xAxis | scrollingStartLocation _ sensor cursorPoint. xAxis _ marker center x. "we constrain the cursor to fall on this xAxis" Cursor cursorLink: false. Cursor upDown showWhile: [ [sensor redButtonPressed] whileTrue: [ cursorPoint _ sensor cursorPoint. cursorPoint ~= oldCursorPt ifTrue: [oldCursorPt _ cursorPoint. sensor cursorPoint: xAxis @ cursorPoint y]. cursorPoint y = scrollingStartLocation y ifTrue: [Cursor upDown show] ifFalse: [cursorPoint y < scrollingStartLocation y ifTrue: [Cursor upArrowHead show] ifFalse: [Cursor downArrowHead show]. self scrollFrom: scrollingStartLocation relativeTo: cursorPoint]. Processor yield ] ]. Cursor cursorLink: true. "ensure the cursor doesn't leap out of the scroll bar" sensor cursorPoint: xAxis @ cursorPoint y! scrollView: anInteger "If anInteger is not zero, tell the reciever's view to scroll by anInteger amount." anInteger ~= 0 ifTrue: [view scrollBy: 0 @ (anInteger max: (view window top - view boundingBox bottom) min: (view window top - view boundingBox top)). view clearInside. view display]! ! !ScrollController methodsFor: 'marker adjustment'! computeMarkerRegion "Answer the rectangular area in which the gray area of the scroll bar should be displayed." ^0@0 extent: MarkerRegionWidth @ ((view window height asFloat / view boundingBox height * scrollBar inside height) rounded min: scrollBar inside height)! moveMarker: anInteger "Update the marker so that is is translated by an amount corresponding to a distance of anInteger, constrained within the boundaries of the scroll bar." Display fill: marker mask: scrollBar insideColor. marker _ marker translateBy: 0 @ (anInteger max: (scrollBar inside top - marker top) min: (scrollBar inside bottom - marker bottom)). marker displayOn: Display! ! !ScrollController methodsFor: 'private'! scrollJumpUp | cursorPoint oldCursorPoint xAxis arrow background | xAxis _ marker center x. oldCursorPoint _ xAxis @ sensor cursorPoint y. background _ self displayArrowAt: oldCursorPoint. Cursor blank showWhile: [ [sensor blueButtonPressed] whileTrue: [ cursorPoint _ xAxis @ (sensor waitForMouseChange y max: scrollBar inside top min: scrollBar inside bottom). cursorPoint ~= oldCursorPoint ifTrue:[ background display. background _ self displayArrowAt: cursorPoint. oldCursorPoint _ cursorPoint]. ] ]. background display. sensor cursorPoint: oldCursorPoint. self canScroll ifTrue: [self scrollViewUp. self moveMarker]! setInsideColor: aMask marker insideColor: aMask! ! !ScrollController class methodsFor: 'initialize'! initializePreferences "This sets up the class variables which are used for widths, colour" self regionWidth: 20. self insideColorMask: Form lightGray. self scrollSpeed: 3 "ScrollController initializePreferences"! insideColorMask: aMask InsideColorMask _ aMask. ScrollController allInstancesAndSubInstancesDo: [:aScrollController | aScrollController setInsideColor: aMask] "ScrollController insideColorMask: Form lightGray"! regionWidth: aWidth RegionWidth _ aWidth. MarkerRegionWidth _ aWidth - 2. "ScrollController regionWidth: 25"! scrollSpeed: anInteger "change this constant to suit: it affects the speed of the scroll" ScrollSpeed _ anInteger "ScrollController scrollSpeed: 3"! ! !ParagraphEditor methodsFor: 'marker adjustment'! computeMarkerRegion paragraph compositionRectangle height = 0 ifTrue: [^0@0 extent: MarkerRegionWidth @ scrollBar inside height] ifFalse: [^0@0 extent: MarkerRegionWidth @ ((paragraph clippingRectangle height asFloat / self scrollRectangleHeight * scrollBar inside height) rounded min: scrollBar inside height)]! ! !ListController methodsFor: 'marker adjustment'! computeMarkerRegion | viewList | viewList _ view list. ^ 0@0 extent: MarkerRegionWidth@ ((viewList clippingRectangle height asFloat / viewList compositionRectangle height * scrollBar inside height) rounded min: scrollBar inside height)! !ScrollController initializePreferences! -------------------------------------------------------------------------- ------------------------------------------------------------------------ Dept. of Computer Science ARPA: miw%uk.ac.man.cs.ux@cs.ucl.ac.uk The University USENET: mcvax!ukc!man.cs.ux!miw Manchester M13 9PL JANET: miw@uk.ac.man.cs.ux U.K. 061-273 7121 x 5699 ------------------------------------------------------------------------