'From etoys4.0 of 9 October 2008 [latest update: #2280] on 11 September 2009 at 2:02:17 am'! "Change Set: ScratchPlugin-jm Date: 11 September 2009 Author: John Maloney (packaged by Bert Freudenberg) This is the (class-side of the) ScratchPlugin class which provides primitive accessors to the ScratchPlugin. Reused with permission from the ScratchCode1.3.1 release. Thanks to Mitchel and John and the other Lifelong Kindergardeners!!"! Object subclass: #ScratchPlugin instanceVariableNames: '' classVariableNames: 'TestFileName' poolDictionaries: '' category: 'Scratch'! !ScratchPlugin commentStamp: '' prior: 0! Copyright (c) 2008 Massachusetts Institute of Technology The ScratchPlugin is reused with permission from the ScratchCode1.3.1 release. Thanks to Mitchel Resnick, John Maloney, and the other Lifelong Kindergardeners!! ---------------------------------------------------------------------------------------------------- This plugin combines a number of primitives needed by Scratch including: a. primitives that manipulate 24-bit color images (i.e. 32-bit deep Forms but alpha is ignored) b. primitive to open browsers, find the user's documents folder, set the window title and other host OS functions This plugin includes new serial port primitives, including support for named serial ports. The underlying plugin code can support up to 32 simultaenously open ports. Port options for Set/GetOption primitives: 1. baud rate 2. data bits 3. stop bits 4. parity type 5. input flow control type 6. output flow control type Handshake lines (options 20-25 for Set/GetOption primitives): 20. DTR (output line) 21. RTS (output line) 22. CTS (input line) 23. DSR (input line) 24. CD (input line) 25. RI (input line) ! ]style[(203 926)f2,FBitstreamVeraSans#12.0! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! blurTest: count "self blurTest: 10" | f outBits | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. f display. count timesRepeat: [ outBits _ f bits copy. self primBlur: f bits into: outBits width: f width. f bits: outBits. f display]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! brightnessShiftTest "self brightnessShiftTest" | f fOut shift | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. [Sensor anyButtonPressed] whileFalse: [ shift _ ((Sensor cursorPoint x - Display center x) * 220) // Display width. self primShiftBrightness: f bits into: fOut bits by: shift. fOut display]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! doubleTest "self doubleTest" | f fOut | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ Form extent: (2 * f extent) + 20 depth: 32. self primDouble: f bits w: f width h: f height into: fOut bits w: fOut width h: fOut height x: 9 y: 10. fOut display. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! fishEye: inForm out: outForm power: power | height sz centerX centerY dx dy ang pix width r srcX srcY | "calculate height, center, scales, radius, whirlRadians, and radiusSquared" sz _ inForm bits size. width _ inForm width. height _ sz // width. centerX _ width // 2. centerY _ height // 2. 0 to: width - 1 do: [:x | 0 to: height - 1 do: [:y | dx _ (x - centerX) / centerX asFloat. dy _ (y - centerY) / centerY asFloat. r _ ((dx * dx) + (dy * dy)) sqrt raisedTo: power. r <= 1.0 ifTrue: [ ang _ dy arcTan: dx. srcX _ centerX + ((r * ang cos) * centerX). srcY _ centerY + ((r * ang sin) * centerY)] ifFalse: [ srcX _ x. srcY _ y]. pix _ self primInterpolate: inForm bits width: inForm width x: (srcX * 1024) truncated y: (srcY * 1024) truncated. outForm bits at: ((y * width) + x + 1) put: pix]]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! fisheyeTest "self fisheyeTest" | f fOut | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. f display. 100 to: 300 by: 10 do: [:power | self primFisheye: f bits into: fOut bits width: f width power: power. fOut display]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! fisheyeTest2: power "self fisheyeTest2: 100" | f fOut | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. f display. self primFisheye: f bits into: fOut bits width: f width power: power. fOut display. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! hueShiftTest "self hueShiftTest" | f fOut shift | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. [Sensor anyButtonPressed] whileFalse: [ shift _ ((Sensor cursorPoint x - Display center x) * 380 * 2) // Display width. self primShiftHue: f bits into: fOut bits byDegrees: shift. fOut display]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! interpolationTest: scale "Answer a copy of the given form scaled by the given factor using linear interpolation." "(self interpolationTest: 1.5) display" | scaleP srcForm outExtent fOut w h outW pix outH | scaleP _ scale asPoint. (scaleP x <= 0 or: [scaleP y <= 0]) ifTrue: [self error: 'bad scale factor']. srcForm _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. outExtent _ (srcForm extent * scaleP) truncated. (outExtent x > 1000 or: [outExtent y > 1000]) ifTrue: [self halt: 'result width or height will be > 1000 pixels']. fOut _ Form extent: outExtent depth: 32. w _ srcForm width. h _ srcForm height. outW _ fOut width. outH _ fOut height. 0 to: fOut width - 1 do: [:x | 0 to: fOut height - 1 do: [:y | pix _ self primInterpolate: srcForm bits width: srcForm width x: (x * w * 1024) // outW y: (y * h * 1024) // outH. fOut bits at: ((y * outW) + x + 1) put: pix]]. ^ fOut ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! saturationShiftTest "self saturationShiftTest" | f fOut shift | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. [Sensor anyButtonPressed] whileFalse: [ shift _ ((Sensor cursorPoint x - Display center x) * 220) // Display width. self primShiftSaturation: f bits into: fOut bits by: shift. fOut display]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! scaleTest: scale "self scaleTest: 1.5" | f fOut | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ Form extent: (f extent * scale) rounded depth: 32. self primScale: f bits w: f width h: f height into: fOut bits w: fOut width h: fOut height. fOut display. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! testFileName: aString "Set the name of an image file for testing." "self testFileName: 'hammy.jpg'" "self testFileName: 'JohnMugShotBW.jpg'" TestFileName _ aString. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! waterRipples1ModuleTest "Smalltalk unloadPlugin: self name" "self waterRipples1ModuleTest" | f fOut ripply aArray bArray | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. aArray _ ByteArray new: (f width) * (f height) * 8 withAll: 0. bArray _ ByteArray new: (f width) * (f height) * 8 withAll: 0. [Sensor anyButtonPressed] whileFalse: [ ripply _ Sensor cursorPoint x max: 1. ripply _ (((ripply / fOut width) sqrt) * 16.0) asInteger. ripply < 1 ifTrue: [ripply _ 1]. ripply printString display. self primWaterRipples1: f bits into: fOut bits width: f width dropNum: ripply array1: aArray array2: bArray. fOut displayAt: 10@30]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! waterRipples1ModuleTest: n "Smalltalk unloadPlugin: self name" "self waterRipples1ModuleTest: 100" | f fOut aArray bArray | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. aArray _ ByteArray new: (f width * f height) * 8 withAll: 0. bArray _ ByteArray new: (f width * f height) * 8 withAll: 0. self primWaterRipples1: f bits into: fOut bits width: f width dropNum: n array1: aArray array2: bArray. [Sensor anyButtonPressed] whileFalse: [ self primWaterRipples1: f bits into: fOut bits width: f width dropNum: 1 array1: aArray array2: bArray. fOut displayAt: 10@30]. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! waterRipplesTime: n "Smalltalk unloadPlugin: self name" "self waterRipplesTime: 100" | f fOut aArray bArray | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. aArray _ ByteArray new: (f width * f height) * 8 withAll: 0. bArray _ ByteArray new: (f width * f height) * 8 withAll: 0. self primWaterRipples1: f bits into: fOut bits width: f width dropNum: n array1: aArray array2: bArray. ^ [100 timesRepeat: [ self primWaterRipples1: f bits into: fOut bits width: f width dropNum: 100 array1: aArray array2: bArray]] msecs. ! ! !ScratchPlugin class methodsFor: 'image filters-testing' stamp: 'jm 11/8/2006 05:30'! whirlTest "self whirlTest" | f fOut degrees | f _ (Form fromFileNamed: TestFileName) asFormOfDepth: 32. fOut _ f deepCopy. [Sensor anyButtonPressed] whileFalse: [ degrees _ ((Sensor cursorPoint x - Display center x) * 450 * 2) // Display width. self primWhirl: f bits into: fOut bits width: f width angle: degrees. fOut display]. ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/31/2005 10:01'! closePort: portNum "Close the given port." "self closePort: 1" ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/28/2005 11:11'! isPortOpen: portNum "Answer true if the given serial port is open." "self isPortOpen: 1" ^ false ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/31/2005 10:03'! openPortNamed: portName baud: baudRate "Open the port with the given name at the given baud rate. Answer the port number to use for further operations on the given port or -1 if the port could not be opened." "self openPortNamed: '/dev/cu.USA19QW3b1P1.1' baud: 9600" ^ -1 ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/4/2005 17:24'! portCount "Answer the number of serial ports. Answer 0 if there are no ports or if this primitive fails." "self portCount" ^ 0 ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/26/2005 15:25'! portCountOrNil "Answer the number of serial ports. Answer nil if this primitive fails." "self portCountOrNil" ^ nil ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/26/2005 15:47'! portName: portIndex "Answer the name of the serial port with the given index. Answer nil if there is no port with the given index." "self portName: 1" ^ nil ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 9/12/2005 16:56'! port: portNum getOption: optionNum "Answer the value of the given serial port option, or nil if the port is not open or the option is not defined. See the class comment for the list of options." ^ nil ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 9/12/2005 16:57'! port: portNum setOption: optionNum to: anInteger "Set the given serial port option to the given value. Do nothing if the option is not defined. See the class comment for the list of options." ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/16/2005 15:57'! readPort: portNum into: buffer "Read from the given port into the given ByteArray or String and answer the number of bytes read." "self readPort: 1 into: (ByteArray new: 10)" ^ 0 ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 11/8/2006 19:45'! serialPortOpsAvailable "Answer true if this plugin is available." "self serialPortOpsAvailable" "Smalltalk unloadPlugin: self name" ^ self portCountOrNil notNil ! ! !ScratchPlugin class methodsFor: 'serial port primitives' stamp: 'jm 8/16/2005 16:46'! writePort: portNum data: buffer "Write data from the given ByteArray or String to the given port and answer the number of bytes written." "self writePort: 1 into: (ByteArray new: 10)" ^ 0 ! ! !ScratchPlugin class methodsFor: 'sound buffer utilities' stamp: 'jm 11/22/2006 19:40'! compactSound: aSoundBuffer by: log2 "Collapse the given by cutting in half log2 times." "self compactSound: (SoundBuffer fromArray: (1 to: 100) asArray) by: 3" | buf | buf _ aSoundBuffer. log2 timesRepeat: [buf _ self extractChannelFrom: buf rightFlag: false]. ^ buf ! ! !ScratchPlugin class methodsFor: 'sound buffer utilities' stamp: 'jm 11/21/2006 21:22'! condenseSoundBuffer: aSoundBuffer by: factor "Condense the given SoundBuffer by the given factor. The result is a SoundBuffer 1/factor of the original size in which each sample represents the peak signal value over factor samples of the source." "self condenseSoundBuffer: (SoundBuffer fromArray: (1 to: 100) asArray) by: 10" | result | result _ SoundBuffer newMonoSampleCount: (aSoundBuffer size + factor - 1) // factor. self primCondense: aSoundBuffer into: result by: factor. ^ result ! ! !ScratchPlugin class methodsFor: 'sound buffer utilities' stamp: 'jm 11/21/2006 21:22'! extractChannelFrom: aSoundBuffer rightFlag: rightFlag "Extract one channel from the given stereo sound buffer. If rightFlag is true, extract the right channel; otherwise, extract the left one." "self extractChannelFrom: (SoundBuffer fromArray: #(1 2 3 4)) rightFlag: true" | result | result _ SoundBuffer newMonoSampleCount: aSoundBuffer size // 2. self primExtractChannelFrom: aSoundBuffer into: result rightFlag: rightFlag. ^ result ! ! !ScratchPlugin class methodsFor: 'sound buffer utilities' stamp: 'jm 11/21/2006 21:12'! primCondense: srcSoundBuffer into: dstSoundBuffer by: anInteger "Condense the given SoundBuffer by the given factor storing the rsult into the destination SoundBuffer." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'sound buffer utilities' stamp: 'jm 11/21/2006 20:33'! primExtractChannelFrom: srcSoundBuffer into: dstSoundBuffer rightFlag: rightFlag "Extract the given channel of the source SoundBuffer in the destination SoundBuffer." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'translation' stamp: 'jm 6/22/2007 21:44'! declareCVarsIn: aCCodeGenerator "self translate" super declareCVarsIn: aCCodeGenerator. aCCodeGenerator cExtras: ' #include "scratchOps.h" #include '.! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 10/29/2007 10:30'! isHidden: fullPath "Return true if file or folder with the given path should be hidden from the user. Return false if the primitive fails." "self isHidden: 'testfile.txt'" Smalltalk isMacOSX ifTrue: [^ false]. ((fullPath endsWith: ':\') and: [fullPath size = 3]) ifTrue: [^ false]. ^ self primIsHidden: fullPath ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 1/23/2007 15:59'! pluginAvailable "self pluginAvailable" | f r | f _ Form extent: 1@1 depth: 32. [r _ self primShiftHue: f bits into: f bits byDegrees: 180] ifError: [^ false]. ^ r notNil ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 1/23/2007 15:46'! primGetFolderPath: anInteger "Return the folder path for the given ID. Folder ID's are: 1 home 2 desktop 3 documents 4 my pictures 5 my music. Return the path for the Scratch folder if the primitive fails." "self primGetFolderPath: 1" ^ FileDirectory default pathName ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 6/22/2007 13:28'! primIsHidden: fullPath "Return true if file or folder with the given path should be hidden from the user. Return false if the primitive fails." "self primIsHidden: 'testfile.txt'" ^ false ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 1/23/2007 15:45'! primOpenURL: aString "Open a browser window on the given URL. Do nothing if the primitive fails." "self primOpenURL: 'http://www.google.com'" ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 10/5/2007 09:12'! primSetUnicodePasteBuffer: aByteArray "Set the Mac OS X Unicode paste buffer. The argument is a big-endian UTF-16 Unicode string packed into a ByteArray. Needed to paste strings from Squeak into Second Life's code editor under Mac OS X. Do nothing if the primitive fails." "self primSetUnicodePasteBuffer: ByteArray new" ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 1/23/2007 15:45'! primSetWindowTitle: aString "Set the title of the Scratch window to the given string. Do nothing if the primitive fails." "self primSetWindowTitle: 'hello!!'" ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 4/11/2007 09:02'! primShortToLongPath: aString "Convert the given Windows short-filename path into a long-filename path. On other platforms this primitive will just return the input string." "self primShortToLongPath: 'hello!!'" ^ aString ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 11/8/2006 05:30'! scale: aForm by: scale "Answer a 32-bit deep Form that's aForm scaled by the given factor. Scales using linear interpolation." | srcF scaledF r | srcF _ aForm asFormOfDepth: 32. srcF unhibernate. scaledF _ Form extent: (srcF extent * scale) rounded depth: 32. r _ self primScaleNoFail: srcF bits w: srcF width h: srcF height into: scaledF bits w: scaledF width h: scaledF height. r ifNil: [^ srcF magnify: srcF boundingBox by: scale asFloat smoothing: 1]. ^ scaledF ! ! !ScratchPlugin class methodsFor: 'OS utilities' stamp: 'jm 9/2/2008 16:45'! setUnicodePasteBuffer: aString "Set the Mac OS X Unicode paste buffer to the given Squeak string. Since the source is a Squeak string, there is no need to handle UTF-16 extended (4-byte) characters. However, we must take byte order into account to accomodate both Intel and PowerPC." "self setUnicodePasteBuffer: 'Hello, Unicode!!'" | utf32 s | utf32 _ aString asUTF32. s _ WriteStream on: (ByteArray new: 2 * aString size). Smalltalk isBigEndian ifTrue: [utf32 do: [:u | s nextPut: (u >> 8). s nextPut: (u bitAnd: 255)]] ifFalse: [utf32 do: [:u | s nextPut: (u bitAnd: 255). s nextPut: (u >> 8)]]. self primSetUnicodePasteBuffer: s contents. ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primBlur: inBitmap into: outBitmap width: w "Blur all the non-transparent pixels in the given 32-bit image bitmap, storing the result in outBitmap. The two bitmaps must be the same size. Each call to this primitive does one Gausian blur step." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primDouble: srcBitmap w: srcWidth h: srcHeight into: dstBitmap w: dstWidth h: dstHeight x: dstX y: dstY "Display the source form at double-size onto the destination form at the given location. Fails if the target rectangle does not fit entirely within the destination form." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primFisheye: inBitmap into: outBitmap width: w power: anInteger "Do a fisheye lens transform of the given 32-bit image bitmap by the given power, storing the result in outBitmap. The two bitmaps must be the same size. Power is 0 for no change, > 0 for fisheye, < 0 for black hole effect." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 2/10/2008 11:58'! primInterpolate: aBitmap width: w x: xFixed y: yFixed "Answer the interpolated pixel value from the given 32-bit bitmap with the given width. The coordinates are given as fixed-point integers with 10-bits of fraction. That is, the float values of x and y are multiplied by 1024, then truncated." ^ 0! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primScaleNoFail: srcBitmap w: srcWidth h: srcHeight into: dstBitmap w: dstWidth h: dstHeight "Scale the source form to exactly fit the destination form using bilinear interpolation. Answer nil if I fail." ^ nil ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primScale: srcBitmap w: srcWidth h: srcHeight into: dstBitmap w: dstWidth h: dstHeight "Scale the source form to exactly fit the destination form using bilinear interpolation." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primShiftBrightness: inBitmap into: outBitmap by: shift "Shift the brightness of all the non-transparent pixels in the given 32-bit image bitmap, storing the result in outBitmap. The shift should be an integer between -100 and 100. The two bitmaps must be the same size." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 1/23/2007 15:58'! primShiftHue: inBitmap into: outBitmap byDegrees: shiftDegrees "Shift the hue of all the non-transparent, non-black pixels in the given 32-bit image bitmap, storing the result in outBitmap. The shift should be an integer between -360 and 360. The two bitmaps must be the same size." self primitiveFailed. ^ nil ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primShiftSaturation: inBitmap into: outBitmap by: shift "Shift the saturation of all the non-transparent, non-black pixels in the given 32-bit image bitmap, storing the result in outBitmap. The shift should be an integer between -100 and 100. The two bitmaps must be the same size." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primWaterRipples1: inBitmap into: outBitmap width: w dropNum: aNum array1: aArray array2: bArray "Apply the water ripple effect to inBitmap putting the result into outBitmap. The two bitmaps must have the same length and are for 32-bit deep Forms of the given width. The dropNum determines how many new water drops are started. The two arrays hold the state of the water-surface model." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'image filter primitives' stamp: 'jm 11/8/2006 10:58'! primWhirl: inBitmap into: outBitmap width: w angle: anAngle "Whirl all the non-transparent pixels in the given 32-bit image bitmap by the given angle, storing the result in outBitmap. The two bitmaps must be the same size." self primitiveFailed ! ! !ScratchPlugin class methodsFor: 'primitive failure' stamp: 'jm 1/23/2007 15:53'! primitiveFailed "Just beep rather than bringing up an error notifier." self beep. ! !