System Class Library
Numbers
Create a number
Numbers are typically created by literal numeric expressions, or by mathematical operations on numbers. Numbers in VisualWorks are real objects, instances of classes `Integer` (really its subclasses `SmallInteger` , `LargePositiveInteger` , and `LargeNegativeInteger` ), `Float` , `Fraction` , and `FixedPoint` .
The following expressions create these numeric types and assign them to variables:
x := 100 "Integer" x := 5.3. "Float" x := 5.5d. "Double" x := 1/2. "Fraction" x := 99.95s. "FixedPoint" x := 1.555e3. "Float, in exponential notation" y := -3.955d2. "Double in exponential notation" x := 2r101 "101 base 2" y := 16r1A "1A base 16"
Arithmetic operations
The following expressions perform standard arithmetic operations:
x + y "Addition" x - y "Subtraction" x * y "Multiplication" x / y "Division" x // y "Divide, return integer part" x \\ y "Divide, return remainder"
Round
The following expressions perform rounding operations:
x := -5.5 rounded "Round to nearest integer" x := 5.8 floor "Round down" x := 5.2 ceiling "Round up" x := -5.8 truncated "Round toward zero" x := 5555.55555 roundTo: 0.01 "Round to nearest multiple of" x := 5555.55555 truncateTo: 0.01 "Truncate to nearest multiple of"
Mathematical operations
The following expressions perform standard arithmetic operations:
3 squared "Square" 9 sqrt "Square root" 5 ** 3 "Raise to power" 125 ** (1/3) "Root"
Testing for equality
The following expressions perform various tests for equality:
5.5 = 5.5 "Equality" 5 == 5 "Identity" 5 ~= 7 "Not equal" 5 ~~ 6 "Not identical" 5 < 6 "Less than" 5<= 6 "Less than or equal" 6 > 5 "Greater than" 6 >= 5 "Greater than or equal" 5 between: 1 and: 10 "Inclusion in range"
Get the minimum/maximum of two numbers
The following expressions perform minimum/maximum operations:
5.5 min: 6.5 "Return the minimum" 5.5 max: 6.5 "Return the maximum"
Trigonometric functions
The following expressions perform trigonometric operations:
x sin "Sine" x cos "Cosine" x tan "Tangent" x arcSin "Arcsine" x arcCos "Arccosine x arcTan "Arctangent"
Logarithmic functions
The following expressions perform logarithmic operations:
x log "Base 10 log" x log: 7 "Base n log" x ln "Natural log" x exp "Exponential"
Test for number
To test if an object is a number of some kind, send a `respondsToArithmetic` message to the object.
If the object is a number, it responds `true` .
"Print it" | x | x := 55. ^x respondsToArithmetic
Test for integer
To test if a number is an integer, send an `isInteger` message to the number.
| x | x := 55. ^x isInteger
Test for even or odd number
To test if a number is even, send an `even` message to the number.
| x | x := 56. ^x even
To test if a number is odd, send an `odd` message to the number.
| x | x := 55. ^x odd
Test for zero
To test if a number is zero, send an `isZero` message to the number.
| x | x := 0. ^x isZero
Test for zero or greater
Send a `positive` message to the number.
| x | x := 55. ^x positive
Test for greater than zero
Send a `strictlyPositive` message to the number.
| x | x := 55. ^x strictlyPositive
Test for less than zero
Send a `negative` message to the number.
| x | x := -55. ^x negative
Get a number's sign as a multiplier
Send a `sign` message to the number. The returned value is `1` when the number is greater than zero, `-1` when the number is less than zero, or 0.
| x y | x := -55. y := x abs. ^y * (x sign)
Reverse a number's sign
Send a `negated` message to the number.
| x | x := -55. ^x negated
Change a number's type
To change between many types of numbers, you merely send the message `asType` to the number, where Type is the number type you wish to change to.
99.95 asFixedPoint: 2. "To fixed point with precision n" 55 asFloat "To single-precision floating point" 55 asDouble "To double-precision floating point" 55.5 asRational "To rational (integer or fraction)"
Get absolute value
Send an `abs` message to the number.
| x | x := -55.5. ^x abs
Get reciprocal
Send a `reciprocal` message to the number.
| x | x := 0.5. ^x reciprocal
Convert between degrees and radians
To convert from degrees to radians, send a `degreesToRadians` message to the number:
x degreesToRadians
To convert from radians to degree, send a `radiansToDegrees` message to the number:
x radiansToDegrees
Convert to a point
Send an `asPoint` message to the number. The returned point has the number as both its `x` and `y` coordinates.
| x | x := 55. ^x asPoint
Create a point
Send an `@` message to a number to be the `x` coordinate with another number as the argument to be the `y` coordinate.
| x y | x := 55. y := 100. ^x @ y
Create a character from a numeric value
Send an `asCharacter` message to an integer. If the receiver is the numeric representation of a valid character, the character is returned; otherwise, an error results.
| x | x := 55. ^x asCharacter
Convert a number to a string
Send a `printString` message to the number.
| x | x := 55. ^x printString
To convert to a string but with a base other than 10, send a `printStringRadix:` message to the number with the base as argument.
| x | x := 55. ^x printStringRadix: 16
Greatest common divisor
Send a `gcd:` message to one of the two numbers. The argument is the second number.
| x y | x := 5. y := 10. ^x gcd: y
Least common multiple
Send an `lcm:` message to one of the two numbers. The argument is the second number.
| x y | x := 5. y := 8. ^x lcm: y
Factorial of a number
Send a `factorial` message to the number.
| x | x := 5. ^x factorial
Random Numbers
Generate random numbers with an instance of `Random` . A random number is a kind of stream, so it responds to stream messages such as next message to get the next number in the sequence.
VisualWorks provides seven different streams of random numbers, identified as generators 1 through 7.
Generate a random number between 0 and 1
Create a random stream of numbers by sending `new` to the `Random` class.
Get the next number in the stream by sending `new` to the random stream.
| randomStream x | randomStream := Random new. x := randomStream next. ^x
Generate a random integer in a specified range
A random stream returns fractional values between 0 and 1. To get a random stream of integers, you must convert the stream values. The example converts random values to a range.
Create a random stream of numbers by sending `new` to `Random` .
Define the beginning and ending values of the range.
Derive the extent of the range.
Get the next value from the random stream, then multiply it by the extent of the range, add the range's beginning value, and round the result.
| randomStream rangeStart rangeEnd rangeExtent x | randomStream := Random new. rangeStart := 1. rangeEnd := 52. rangeExtent := rangeEnd - rangeStart. x := (randomStream next * rangeExtent + rangeStart) rounded. ^x
Reproduce a "random" sequence
To reproduce a specific sequence, you can specify a seed value. the seed value can also be used to generate additional random features.
Create the random stream by sending a `fromGenerator:seededWith:` message to the `Random` class. The first argument is an integer from 1 to 7, identifying one of the seven streams that VisualWorks provides. The second argument is a number that is used to select a position in the stream.
| rangeStart rangeEnd rangeExtent randomStreamsequence | rangeStart := 1. rangeEnd := 52. rangeExtent := rangeEnd - rangeStart.

randomStream := Random fromGenerator: 1 seededWith: 1.
Numeric Constants
Zero
Send a `zero` message to any numeric class. The zero value returned is an instance of the target class.
To get a zero of the same class as an existing number, first get the class of that number by sending a class message to it and then send `zero` to the resulting object.
| x y z | x := Float zero. y := Integer zero. z := x class zero. ^Array with: x with: y with: z
One
Send a `unity` message to any numeric class. The value returned is an instance of the target class.
To get a value of the same class as an existing number, first get the class of that number by sending a `class` message to it and then send `unity` to the resulting object.
| x y z | x := Float unity. y := Integer unity. z := x class unity. ^Array with: x with: y with: z
Pi
Send a `pi` message to the `Float` or `Double` class. Note that `Float` returns a single-precision version while `Double` returns a double-precision version.
To get a pi of the same class as an existing number, first get the class of that number and then send `pi` to the resulting object.
| x y z | x := Float pi. y := Double pi. z := x class pi. ^Array with: x with: y with: z
Get the largest SmallInteger
This value is frequently used to specify an arbitrarily large number whose exact value is not important.
Send a `maxVal` message to the `SmallInteger` class.
| x | x := SmallInteger maxVal. ^x
Get the smallest SmallInteger
Send a `minVal` message to the `SmallInteger` class.
| x | x := SmallInteger minVal. ^x
Dates
Create today's date
Send a `today` message to the `Date` class.
| date | date := Date today. ^date
Create a date from a string
Send a `readFrom:` message to `Date` with a readStream on a string containing the month, day, and year in any of several formats as shown. When the string uses a two-digit abbreviation for the year a `Date` is created for the current century today. To avoid any ambiguity create a `Date` using a four-digit year.
| dates | dates := OrderedCollection new.

dates add: (Date readFrom: 'January 31, 1994' readStream); add: (Date readFrom: '31 January 1994' readStream); add: (Date readFrom: '1/31/94' readStream); add: (Date readFrom: '1.31.1994' readStream); add: (Date readFrom: '1-31-1994' readStream). ^dates
Create a date from a day, month, and year
Send a `newDay:monthNumber:year:` message to the `Date` class. The newDay argument is the day number. The `monthNumber` argument is the month number. The `year` argument is the year, with or without the century part. If a two-digit year is provided then it is applied to the current century today. Use a four-digit year to avoid ambiguity.
date1 := Date newDay: 31 monthNumber: 1 year: 1994.
To specify the month by name, send a `newDay:month:year:` message to `Date` , with the month argument as the unique first letters of a month name expressed as a `Symbol` .
date2 := Date newDay: 31 month: #Jan year: 1994.
Create a date by days since January 1
During a series of date computations that span several months in the same year, it can be helpful to treat a date as the number of days that it represents since the beginning of the year. After the computation is completed, you can convert the day-count back into a date.
Send a `newDay:year:` message to `Date` . The first argument is the number of days from the beginning of the year. The second argument is the year number.
| date | date := Date newDay: 32 year: 1994. ^date
Create a date by days since 1901
During a series of date computations that span multiple years, it can be helpful to treat a date as the number of days that it represents since 1901 (in effect, the beginning of recorded time). After the computation is completed, you can convert the day-count back into a date.
Send a `fromDays:` message to `Date` . The argument is the number of days from the beginning of 1901.
| date | date := Date fromDays: (94 * 366). ^date
Get the day of the week
Send a `weekday` message to a date. The name of the week day is expressed as a `Symbol` , such as `#Friday` .
| date | date := Date today. ^date weekday
Get the day of the month
Send a `dayOfMonth` message to a date. The day number within the month is returned.
| date | date := Date today. ^date dayOfMonth
Get the day of the year
Send a `day` message to a date. The day number within the year is returned.
| date | date := Date today. ^date day
Count the days since 1901 began
Send an `asDays` message to a date. The day number within the century is returned.
| date | date := Date today. ^date asDays
Count the seconds since 1901 began
Send an `asSeconds` message to a date. The number of seconds elapsed prior to the date in the century is returned.
| date | date := Date today. ^date asSeconds
Get the name of the month
Send a `monthName` message to a date. The month name is expressed as a `Symbol` , as in `#January` .
| date | date := Date today. ^date monthName
Get the number of the month
Send a `monthIndex` message to a date.
| date | date := Date today. ^date monthIndex
Count the days in the month
Send a `daysInMonth` message to a date.
| date | date := Date today. ^date daysInMonth
Get the year number from a date
Send a `year` message to a date.
| date | date := Date today. ^date year
Count the days in a year
Send a `daysInYear` message to a date.
| date | date := Date today. ^date daysInYear
Count the days remaining in the year
Send a `daysLeftInYear` message to a date.
| date | date := Date today. ^date daysLeftInYear
Test for leap year
Send a `leap` message to a date. The result is 1 in a leap year and zero otherwise.
| date | date := Date today. ^date leap
Add days to a date
Send an `addDays:` message to a date. The argument is the number of days to be added, and can be a negative number.
| date daysToAdd | date := Date today. daysToAdd := 60. ^date addDays: daysToAdd
Subtract days from a date
Send a `subtractDays:` message to a date. The argument is the number of days to be subtracted, and it can be a negative number.
| date daysToSubtract | date := Date today. daysToSubtract := 60. ^date subtractDays: daysToSubtract
Get the number of days between two dates
Send a `subtractDate:` message to a date. The argument is the date to be subtracted, which can be either before or after the first date.
| date1 date2 | date1 := Date today. date2 := Date readFromString: '31 December 1999'. ^date1 subtractDate: date2
Get a previous day of the week
Send a `previous:` message to a date. The argument is the name of the preceding weekday whose date you desire, expressed as a `Symbol` .
| date dayOfWeek | date := Date today. dayOfWeek := #Monday. ^date previous: dayOfWeek
Test/compare dates
Dates respond to many of the same test methods as numbers. Return values are booleans.
date1 = date2 "equality" date1 ~= date2 "non-equality" date1 < date2 "earlier than" date1 <= date2 "earlier or equal" date1 > date2 "later than date1 >= date2 "later or equal"
Format a Date
A date can be formatted by specifying an array of format arguments sent with the `printFormat:` message. The six elements are:
Day's position in the string (1, 2, or 3)
Month's position in the string (1, 2, or 3)
Year's position in the string (1, 2, or 3)
The separator character
Month's format: 1 (numeric), 2 (abbreviation), or 3 (full name)
Year's format: 1 (with century) or 2 (without century)
Send a `printFormat:` message to the date. The argument is an array of six elements.
| date | date := Date today. ^date printFormat: #(2 1 3 $- 3 1)
Times
Create the current time
Send a `now` message to the `Time` class.
| time | time := Time now. ^time
Create a time from a string
Send a `readFromString:` message to `Time` . The argument is a string containing the hours, minutes, and seconds, separated by colons. The minutes and/or seconds can be omitted. The "am/pm" designation can be omitted ("am" is the default) and can be in upper- or lowercase.
| times | times := OrderedCollection new.

times add: (Time readFromString: '3:47:26 pm'); add: (Time readFromString: '03:47'); add: (Time readFromString: '::26 PM'). ^times
Create a time by seconds since midnight
Send a `fromSeconds:` message to `Time` . The argument is the number of seconds that have elapsed since midnight.
| time | time := Time fromSeconds: (60 * 60 * 4). ^time
Get seconds since the minute began
Send a `seconds` message to the time.
| time | time := Time now. ^time seconds.
Get seconds since the day began
Send an `asSeconds` message to the time.
| time | time := Time now. ^time asSeconds.
Get seconds since 1901
In time computations that span multiple days, it is sometimes convenient to represent each time as the number of seconds since 1901 began.
Send a `totalSeconds` message to `Time` .
| x | x := Time totalSeconds. ^x
Getting seconds since the clock was reset
When you want to measure the number of milliseconds required by some process, you can take a reading of the millisecond clock both before and after the process. This reading is also sometimes used as a simple random number for temporary file naming and as a seed value for a random stream.
Send a `millisecondClockValue` message to `Time` .
"Time 1000 repetitions of a block" | x | x := Time millisecondClockValue. 1000 timesRepeat: [|splitSecondCount| "Answer milliseconds as a number between 0 and 999" splitSecondCount:=Time millisecondClockValue \\ 1000. splitSecondCount < 250 ifTrue:["Spend no more than one quarter of any second in this block. Show the millisecond count in the Transcript" Transcript show: splitSecondCount printString;cr]]. ^Time millisecondClockValue - x
Get minutes since the hour began
Send a `minutes` message to an instance of class `Time` .
| time | time := Time now. ^time minutes.
Get hours since the day began
Send an `hours` message to an instance of class `Time` .
| time | time := Time now. ^time hours.
Add times and dates
Send an `addTime:` message to an instance of class `Time` . The argument is either a `Time` or a `Date` .
| time1 time2 | time1 := Time readFromString: '5'. time2 := Time readFromString: '8:51:39 am'. ^time1 addTime: time2
Subtract times and dates
Send an `subtractTime:` message to an instance of class `Time` . The argument is either a `Time` or a `Date` .
| time1 time2 | time1 := Time readFromString: '5'. time2 := Time readFromString: '8:51:39 am'. ^time2 subtractTime: time1
Create a time stamp
A time stamp is a common way to record the date and time that an event occurred. The `Time` class provides a `dateAndTimeNow` method returns an array containing two elements: the current date and the present time.
Send a `dateAndTimeNow` message to the `Time` class.
| dateTime | dateTime := Time dateAndTimeNow. ^(dateTime at: 1) printString, ' ', (dateTime at: 2) printString.
Time a block of code
During the optimization phase of application development, it is useful to compare the run times of alternate implementations. A profiler tool, such as that provided in the Advanced Tools parcel, provides this information. A simpler utility is provided by the `Time` class `millisecondsToRun:` method. This method may be used to determine the clock time in milliseconds to execute a block containing one or more Smalltalk statements.
Create a `BlockClosure` containing one or more expressions to be tested. Repeating the expressions through `timesRepeat:` usually improves the validity of the comparison.
Send a `millisecondsToRun:` message to the `Time` class. The argument is the block you created in step 1.
Repeat steps 1 and 2 for the second version of the code. If the second version is not ready yet, you can simply record the value from step 2 for later comparison.
| block1 block2 ms1 ms2 |

"Test the speed of Time now and Date today." block1 := [100 timesRepeat: [Time now. Date today]]. ms1 := Time millisecondsToRun: block1.

"Test the speed of dateAndTimeNow, which does the same thing." block2 := [100 timesRepeat: [Time dateAndTimeNow]]. ms2 := Time millisecondsToRun: block2.

^ms1 printString, ' ', ms2 printString
Install a time zone as the system default
On machines that report Greenwich Mean Time (GMT) rather than local time, the `Time` class converts GMT to local time with the aid of another class, `TimeZone` . A `TimeZone` stores an offset from GMT for local time.
Create a new instance of `TimeZone` by sending a `timeDifference:DST:at:from:to:startDay:` message to the `TimeZone` class, where:
timeDifference
is the offset in hours from GMT.
DST
is the number of hours by which DST differs.
at
is the number of hours after midnight at which DST begins and ends.
from
is the number of the day on which DST begins in a nonleap year (it will be adjusted automatically during leap years).
to
is the number of the day on which DST ends in a nonleap year.
startDay
is the day of the week on which DST begins and ends.
Send a `setDefaultTimeZone:` message to the `TimeZone` class. The argument is the time zone you created in step 1.
With the time zone set, se the Launcher's File->Save As... command to save your image.
| newZone | newZone := TimeZone timeDifference: -8 DST: 1 at: 2 from: 97 to: 304 startDay: #Sunday.

TimeZone setDefaultTimeZone: newZone.
Install a null time zone
A null time zone does not alter the time returned by your computer.
Send a `setDefaultTimeZone:` message to the `TimeZone` class. The argument is the time zone you created in step 1.
With the time zone set, se the Launcher's File->Save As... command to save your image.
| nullZone | nullZone := TimeZone null. TimeZone setDefaultTimeZone: nullZone.
Set the time zone weekday
Get the default time zone by sending a `default` message to the `TimeZone` class.
Send a `weekDayToStartDST` message to the default time zone. The argument is the name of the day on which DST is to begin and end, in the form of a `Symbol` .
With the time zone set, se the Launcher's File->Save As... command to save your image.
| zone | zone := TimeZone default. zone weekDayToStartDST: #Saturday.
Collections
Create an empty collection
Send a `new` message to the desired collection class (in the example, `List` ).
| list | list := List new.

list add: 'Leonardo'; add: 'Michelangelo'; add: 'Donatello'; add: 'Raphael'. ^list.
Create a collection of a set size
Send a `new:` message to the desired collection class (typically `Array` , but useful with other types of collections to avoid time-consuming grow operations as elements are added).
| array | array := Array new: 4.

array at: 1 put: 'Leonardo'; at: 2 put: 'Michelangelo'; at: 3 put: 'Donatello'; at: 4 put: 'Raphael'. ^array.
Create an initialized collection
Send a `new:withAll:` message to the desired collection class, with the initialization object.
Array new: 16 withAll: 0.
Create an array with up to four elements
Send a `with:with:with:with:` message to the `Array` class, or a variant of that message containing as many `with:` keywords as needed, up to four. The argument of each `with:` keyword can be any object.
array1 := Array with: 'Leonardo'.

array2 := Array with: 'Leonardo' with: 'Michelangelo'.
Create a literal array
Enclose the list of literal elements in parentheses, with a number-sign prefix. Any white-space character can be used to separate the elements.
#( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael' )
Create a new collection from an old collection
Send a `withAll:` message to the desired collection class (`List` ).
List withAll: Smalltalk keys
Get a collection's size
This is the number of used slots in the collection. The number of available slots is the capacity. Send a `size` message to the collection. The response is an integer (in the example, `31` ).
| array | array := ColorValue constantNames. ^array size
Get a collection's capacity
This is the total number of slots available. They may not all be used. The capacity can be expanded by growing the collection. Send a `capacity` message to the collection. The response is an integer.
| set | set := Set withAll: ColorValue constantNames. ^set capacity
Test if a collection is empty
Send an `isEmpty` message to the collection. The response is `true` when the collection has no elements and `false` otherwise.
| list | list := List allInstances.

list isEmpty ifFalse: [^list first]
Add elements to a collection
A `List` , `Set` , or `Dictionary` can add elements at any time.
Send an `add:` message to a `List` or `Set` . The argument can be any object.
Send an `at:put:` message to a `Dictionary` . The first argument is the lookup key, typically but not necessarily a `Symbol` . The second argument is the object that is associated with the key.
| list dict | list := List new. dict := Dictionary new.

list add: 'Leonardo'; add: 'Michelangelo'; add: 'Donatello'; add: 'Raphael'.

dict at: #Leader put: 'Leonardo'; at: #Member1 put: 'Michelangelo'; at: #Member2 put: 'Donatello'; at: #Member3 put: 'Raphael'.

^Array with: list with: dict
Insert an element at a location
Send an `addFirst:` message to a `List` . The argument is the element to be inserted at the beginning of the collection.
Send an `add:before:` message to a `List` . The first argument is the element to be inserted. The second argument is the element before which the insertion is to take place.
Send an `add:beforeIndex:` message to a `List` . The first argument is the element to be inserted. The second argument is the index of the element before which the insertion is to take place.
| list | list := List new.

list add: 'Raphael'; addFirst: 'Leonardo'; add: 'Michelangelo' before: 'Raphael'; add: 'Donatello' beforeIndex: 3.

^list
Add a collection of elements
Send an `addAll:` message to a `List` or `Set` . The argument is a collection of elements to be added. Remember that a `Set` will discard duplicate elements.
Send an `addAllFirst:` message to a `List` (not a `Set` ). The argument is the collection of elements to be inserted at the beginning of the list.
Send an `addAll:beforeIndex:` message to a `List` . The first argument is the collection to be inserted. The second argument is the index number of the element before which the new batch is to be inserted.
| sizes totalElements | sizes := List new: 10000.

sizes addAll: (List allInstances collect: [ :list | list size]). sizes addAllFirst: (Dictionary allInstances collect: [ :dict | dict size]). sizes addAll: (Array allInstances collect: [ :array | array size]) beforeIndex: 2.

totalElements := 0. sizes do: [ :sz | totalElements := totalElements + sz]. ^totalElements
Expand an array
Send a `copyWith:` message to a `List` or `Array` . The argument is the object that is to be appended to the end of the copy.
Replace the original array with the expanded copy.
| array copy | array := #( 1 2 3 4 5 6 7 8 9 ).

copy := array copyWith: 10. array := copy. ^array
Remove an element
Send a `remove:` message to a `List` or `Set` . The argument is the object to be removed.
| list | list := List withAll: ColorValue constantNames.

list remove: #red. ^list
Supply an element-not-found response
Send a `remove:ifAbsent:` message to a `List` or `Set` . The first argument is the object to be removed. The second argument is a block containing the action or actions.
An empty block is an effective means of taking no action-- that is, simply shutting off the error notifier that is displayed by default.
| list | list := List withAll: ColorValue constantNames.

list remove: #brickRed ifAbsent: [Dialog warn: 'You must be kidding -- brickRed?'].

list remove: #moonbeam ifAbsent: [ ]. ^list
Remove a subset
Send a `removeAll:` message to a `List` or `Set` . The argument is a collection containing the elements to be removed. If an element is not found, an error results. The subset can be contained in a different type of collection.
| list | list := List withAll: ColorValue constantNames.

list removeAll: #( #red #green #blue ). ^list
Remove elements by index
Send a `removeFirst` message to a `List` (but not a `Set` , `Array` , or `Dictionary` ). The first element in the list will be removed. If the list is empty, an error results.
Send a `removeFirst:` message to a `List` . The argument is the number of elements to be removed from the front of the list.
Send a `removeLast` message to remove the last element.
Send a `removeLast:` message. The argument is the number of elements to be removed from the end of the list.
Send a `removeFrom:to:` message to a `List` . The first argument is the starting index of the range and the second argument is the ending index. An array containing the deleted elements is returned.
Send a `removeFrom:to:returnElements:` message to a `List` . The first and second arguments are index numbers identifying the range to be removed. The third argument is `false` when you want `nil` to be returned instead of an array containing the deleted elements--for large removal operations, this is more efficient.
| list | list := List new: 25. 1 to: 25 do: [ :i | list add: i].

"Removes 1" list removeFirst.

"Removes 2 3 4 5 6" list removeFirst: 5.

"Removes 25" list removeLast.

"Removes 20 21 22 23 24" list removeLast: 5.

"Removes 14 15 16 17 18" list removeFrom: 8 to: 12.

"Removes 9 10 11 12 13" list removeFrom: 3 to: 7 returnElements: false.

^list
Remove elements that pass a test
Send a `removeAllSuchThat:` message to a `List` . The argument is a block containing the test. The block must declare one argument variable for the element to be tested.
| list | list := List withAll: ColorValue constantNames.

list removeAllSuchThat: [ :name | name first == $r]. ^list
Remove an association from a dictionary
Send a `removeKey:` message to the dictionary. The argument is the key of the association that you want to remove. The removed value is returned. If the key is not found, an error results.
To provide an alternative response to the key-not-found condition, send a `removeKey:ifAbsent:` message to the dictionary. The first argument is the key to be removed and the second argument is a block that specifies the action to take if the key is not found. An empty block causes no action, which is the same as silently ignoring the condition.
| dict | dict := Dictionary new. dict at: #Leader put: 'Leonardo'; at: #Member1 put: 'Michelangelo'; at: #Member2 put: 'Donatello'; at: #Member3 put: 'Raphael'.

dict removeKey: #Member2.

dict removeKey: #Villain ifAbsent: [ ]. ^dict
Remove an element from an array
Send a `copyWithout:` message to an `Array` (or a `List` ). The argument is the object to be removed. Every occurrence of that object will be removed from the copy.
Replace the original array with the copy.
| array copy | array := #( 1 8 3 4 5 6 7 8 9 ).

copy := array copyWithout: 8. array := copy. ^array
Replace elements
Send an `at:put:` message to the collection.
For a `Dictionary` , the first argument is the lookup key and the second argument is the value to be placed at that key. If the key does not exist, it will be added.
For a `List` or`Array` , the first argument is the index number of the element to be replaced, and the second argument is the object that is to replace the old element.
| list dict | dict := Dictionary new. dict at: #Leader put: 'Leonardo'; at: #Member1 put: 'Michelangelo'; at: #Member2 put: 'Donatello'; at: #Member3 put: 'Raphael'. list := List withAll: dict values. list sort.

dict at: #Leader put: 'Rembrandt'.

list at: 1 put: 'Rembrandt'.

^Array with: list with: dict.
Replace all elements
Send an `atAllPut:` message to a `List` or `Array` . The argument is the object that is to replace all existing elements.
| list | list := List new. 1 to: 10 do: [ :number | list add: number ].

list atAllPut: 0. ^list
Replace specified elements
Send an `atAll:put:` message to a `List` or `Array` . The first argument is a collection containing the index numbers of the elements to be replaced. The second argument is the object to be placed in those slots.
| list | list := List new. list add: 'red'; add: 'ghoulishGreen'; add: 'red'; add: 'blackAndBlue'.

list atAll: #( 1 3) put: 'bloodRed'. ^list
Replace all occurrences of an object
Send a `replaceAll:with:` message to a `List` or `Array` . The first argument is the object whose occurrences you want to replace. The second argument is the replacement object.
| list | list := List new. list add: 'red'; add: 'ghoulishGreen'; add: 'red'; add: 'blackAndBlue'.

list replaceAll: 'red' with: 'bloodRed'. ^list
Replace a subset with a new subset
Send a `replaceFrom:to:with:startingAt:` message to a `List` or `Array` . The first and second arguments are index numbers identifying the replacement range. The `with:` argument is a collection containing the new elements. The `startingAt:` argument is the index number in the new collection at which to begin copying the replacement elements.
| mainList replacements | mainList := #( 1 2 3 4 5 6 7 8 9 ). replacements := #( 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 ).

mainList replaceFrom: 1 to: mainList size with: replacements startingAt: 7.

^mainList
Copy elements
When copying a collection, you can modify literal elements such as numbers and strings without affecting the copy. For a nonliteral element, the copied collection holds onto the original object rather than a copy, and will reflect any changes you make to that object.
Send a `copy` message to the collection.
| dict1 dict2 | dict1 := Dictionary new. dict1 at: #Leader put: 'Leonardo'; at: #Member1 put: 'Michelangelo'; at: #Member2 put: 'Donatello'; at: #Member3 put: 'Raphael'.

dict2 := dict1 copy.

"Change the original without changing the copy." dict1 removeKey: #Leader. dict1 at: #Leader put: 'Rembrandt'.

^Array with: dict1 with: dict2
Copy a subset
Send a `copyFrom:to:` message to a `List` or `Array` . The first argument is the starting index of the range you want to copy, and the second argument is the ending index.
| list copy | list := List new. 1 to: 10 do: [ :number | list add: number ].

copy := list copyFrom: 1 to: 3. ^copy
Combine two collections
Two ordered collections, such as a `List` and an `Array` , can be combined, as shown in the basic step. This technique is widely used with strings (which are ordered collections of characters), but it can also be used with other kinds of ordered collections.
Send a comma (,) message to a `List` or `Array` . The argument is another ordered collection. A new collection will be returned, of the same type as the first collection, containing the elements of both collections.
| list array combinedList | list := List withAll: ColorValue constantNames. array := #( #bloodRed #ghoulishGreen #blackAndBlue ).

combinedList := list, array. ^combinedList
Find elements
Send an `at:` message to a `List` or `Array` . The argument is an index number. If the object is not found, zero is returned.
| list | list := List withAll: ColorValue constantNames.

^list at: 1
Search a dictionary
Send an `at:` message to the dictionary. The argument is the lookup key. If the key does not exist, an error results.
To avoid the key-not-found error, send an `at:ifAbsent:` message. The second argument is an empty block (for no action) or a block containing actions to be taken if the key does not exist.
To find the key that corresponds to a value, send a `keyAtValue:ifAbsent:` message to the dictionary. The first argument is the object whose key is to be found. The second argument is a block containing the value-not-found action.
| dict found1 found2 found3 | dict := Smalltalk.

found1 := dict at: #List. found2 := dict at: #UnlikelyClassName ifAbsent: [nil]. found3 := dict keyAtValue: List ifAbsent: [nil].

^Array with: found1 with: found2 with: found3
Get the index of an object
Send an `indexOf:` message to a `List` or `Array` . The argument is the object to be found. If the object is not an element, zero is returned.
To search a subset of the `List` or `Array` , send a `nextIndexOf:from:to:` message. The first argument is the object to be found. The second and third arguments are indexes that define the search range. The returned index is relative to the beginning of the collection.
To search backward from the end, send a `lastIndexOf:` message. The index of the last occurrence is returned, or zero if none exists. The returned index is relative to the beginning of the collection.
| list found1 found2 found3 | list := List withAll: #( #red #green #blue #red #yellow #blue).

found1 := list indexOf: #red. found2 := list nextIndexOf: #red from: 2 to: 6. found3 := list lastIndexOf: #red.

^Array with: found1 with: found2 with: found3
Find an object in a collection
To find out if an object is in a collection, send an `includes:` message to the collection, with the object as argument. The return value is `true` if the object is in the collection, and `false` otherwise.
To find out how many instances of an object are in a collection, send an `occurrencesOf:` message to the collection, with the object as argument. The return value is the number of occurrences.
| list found1 found2 | list := List withAll: #( #red #green #blue #red #yellow #blue).

found1 := list includes: #red. found2 := list occurrencesOf: #red.

^Array with: found1 with: found2
Get the element before or after an object
Send a `before:` message to a `List` . The argument is the element before which the desired element is located. If the argument matches the first element, an error results.
Send an `after:` message to a `List` . The argument is the element after which the desired element is located. If the argument matches only the last element, an error results.
| list found1 found2 | list := List withAll: #( #red #green #blue #red #yellow #blue).

found1 := list before: #blue. found2 := list after: #yellow.

^Array with: found1 with: found2
Find the first or last element
To get the first element, send a `first` message to a `List` or `Array` . If the collection is empty, an error results.
To get the last element, send a `last` message to a `List` or `Array` . If the collection is empty, an error results.
| list found1 found2 | list := List withAll: #( #red #green #blue).

found1 := list first. found2 := list last.

^Array with: found1 with: found2
Find a subset (sub-collection)
Send an `indexOfSubCollection:startingAt:` message to a `List` or `Array` . The first argument is the sub-collection to be found, which need not be the same type of collection. The second argument is the index number at which the search is to begin. The returned index number is relative to the beginning of the collection.
If the sub-collection is not found, zero is returned. `indexOfSubCollection:startingAt:` searches for the sub-collection that appears in the same order as the target and with no elements between its elements.
| list found1 found2 found3 | list := List withAll: #( #red #green #blue #red #yellow #blue).

found1 := list indexOfSubCollection: #( #red #yellow #blue) startingAt: 1. found2 := list indexOfSubCollection: #( #yellow #red) startingAt: 1. found3 := list indexOfSubCollection: #( #red #blue) startingAt: 1. ^Array with: found1 with: found2 with: found3
Find elements that pass or fail a test
To collect all elements that pass a test, send a `select:` message to a collection. The argument is a one-argument block containing the selection test.
To collect all elements that fail a test, send a `reject:` message to a collection. The argument is a one-argument block containing the rejection test.
To get the first element that passes a test, send a `detect:ifNone:` message to a collection. The first argument is a one-argument block containing the test. The second argument is a no-argument block containing the action to take if no element passes the test.
| list found1 found2 found3 | list := List withAll: Smalltalk classNames.

"Select classes with 'Example' in their names." found1 := list select: [ :nextElement | (nextElement indexOfSubCollection: 'Example' startingAt: 1) > 0].

"Reject classes with 'Example' in their names." found2 := list reject: [ :nextElement | (nextElement indexOfSubCollection: 'Example' startingAt: 1) > 0].

"Detect the first class beginning with 'R'." found3 := list detect: [ :nextElement | nextElement first == $R] ifNone: [0].

^Array with: found1 with: found2 with: found3
Test whether collections are the same
A collection can be compared to another collection. The collections can be compared for either equality of elements or identity of elements.
To test for equality of elements, send a `=` message to one of the collections with the other collection as the argument. to test for identity of elements, send a `==` message.
| list1 list2 copyIsEqual copyWithChangedElementIsEqual | list1 := List withAll: ColorValue constantNames. list2 := list1 copy.

copyIsEqual := list1 = list2.

list2 at: 1 put: #burntOrange. copyWithChangedElementIsEqual := list1 = list2.

^Array with: copyIsEqual with: copyWithChangedElementIsEqual.
Subtracting one set from another
Send a - (minus) message to a `Set` or `Dictionary` . The argument is another set or dictionary. A similar type of collection is returned, containing the elements that occur in the first set but not the second.
| set1 set2 | set1 := Set withAll: ColorValue constantNames. set2 := set1 select: [ :name | (name indexOfSubCollection: 'light' startingAt: 1) > 0].

^set1 - set2
Sort a collection in ascending order
A sorted collection can be sorted, as long as its elements respond to `<` and `=` messages.
Send a `sort` message to a sorted collection, such as a `List` .
| list | list := List withAll: #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael').

list sort. ^list
Define a sort order
Send a `sortWith:` message to a sorted collection. The argument is a block containing the test for determining whether one element precedes another. The block must declare two arguments to contain the two elements being compared. The block should return `true` if the first argument should precede the second. The block should return `false` otherwise. (In the example, the test causes the elements to be sorted in descending order.)
| list | list := List withAll: #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael').

list sortWith: [ :element1 :element2 | element1 > element2]. ^list
Make an unsorted collection sortable
Send an `asSortedCollection` message to the collection. A `SortedCollection` is returned, with the collection's elements in ascending order.
To customize the sort order, send an `asSortedCollection:` message to the collection. The argument is a block that compares two elements and answers `true` when the first element is to precede the second element.
| array1 sort1 array2 sort2 |

array1 := #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael'). sort1 := array1 asSortedCollection. array2 := #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael'). sort2 := array2 asSortedCollection: [ :name1 :name2 | name1 > name2].

^Array with: sort1 with: sort2.
Reverse the order of elements
Send a `reverse` message to a `List` or `Array` (or any ordered collection). A new instance of the same type of collection is returned, with the elements in reverse order (but not explicitly sorted).
| array reversedArray | array := #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael').

reversedArray := array reverse. ^reversedArray
Change collection classes
Any collection can be converted to a `List` , an `Array` , or a `Set` , by sending an `asList` , `asArray` , or `asSet` message to the collection. The original collection is not converted, but an instance of the desired type of collection is returned containing the original collection's elements, and the original collection remains unchanged.
| array list set | array := ColorValue constantNames.

list := array asList. set := array asSet. array := set asArray. ^Array with: list with: set with: array
Loop through a collection
Send a `do:` message to a collection. The argument is a block that performs a series of operations on an element. The block is repeated for each element in the collection, and it is expected to declare one argument variable to hold the next element to be processed.
| list color | list := List withAll: ColorValue constantNames. list sort.

list do: [ :colorName | Transcript show: colorName asString; cr. color := ColorValue perform: colorName. Transcript show: color red printString; tab; show: color green printString; tab; show: color blue printString; cr; cr].
Loop in reverse order
Send a `reverseDo:` message to a collection. The argument is a block that performs a series of operations on an element. The block is repeated for each element in the collection, starting with the last and proceeding toward the first element. The block is expected to declare one argument variable to hold the next element to be processed.
| list color | list := List withAll: ColorValue constantNames. list sort.

list reverseDo: [ :colorName | Transcript show: colorName asString; cr. color := ColorValue perform: colorName. Transcript show: color red printString; tab; show: color green printString; tab; show: color blue printString; cr; cr].
Loop on lookup keys
Send a `keysDo:` message to an ordered collection such as a `Dictionary` , `List` , or `Array` . The argument is a one-argument block that performs a series of operations using the lookup key for each element.
Send a `keysAndValuesDo:` message to an ordered collection such as a `Dictionary` , `List` , or `Array` . The argument is a two-argument block that performs a series of operations using the lookup key and associated value for each element.
| dict randomGenerator gc randomX randomY colorValue | randomGenerator := Random new. gc := (ExamplesBrowser prepareScratchWindowOfSize: 300@400) graphicsContext.

dict := Dictionary new. ColorValue constantNames do: [ :colorName | colorValue := ColorValue perform: colorName. dict at: colorName put: colorValue].

dict keysDo: [ :colorName | randomX := randomGenerator next * 300. randomY := randomGenerator next * 300. colorName displayOn: gc at: (randomX @ randomY)].

dict keysAndValuesDo: [ :colorName :color | randomX := randomGenerator next * 300. randomY := randomGenerator next * 300. gc paint: color. colorName displayOn: gc at: (randomX @ randomY)].
Collect the results of iteration
Send a `collect:` message to a collection. The argument is a block that processes an element and returns an object that is to become an element in the result collection. The block is expected to declare one argument variable for the next element to be processed.
| list capitalizedName initial | list := List withAll: ColorValue constantNames. list sort.

list collect: [ :colorName | capitalizedName := colorName asString. initial := (capitalizedName at: 1) asUppercase. capitalizedName at: 1 put: initial. capitalizedName].
Looping through two collections
Send a `with:do:` message to a `List` or `Array` . The first argument is another `List` or `Array` of the same size as the first. The second argument is a block that performs a series of operations on a pair of elements, one from each of the two collections. The block is expected to declare two argument variables, one for each of the elements.
| array1 array2 dict | array1 := #( #Leader #Member1 #Member2 #Member3). array2 := #( 'Leonardo' 'Michelangelo' 'Donatello' 'Raphael' ). dict := Dictionary new.

array1 with: array2 do: [ :array1Element :array2Element | dict at: array1Element put: array2Element].

^dict
Characters and Strings
Create a character literal
To create a character as a separate entity, rather than as part of a string, use its literal form preceding the character with a dollar sign.
| char | char := $C. ^char
Create a nondisplaying character
Several nondisplaying characters have `Character` class messages to create them: `backspace` , `cr` , `del` , `esc` , `leftArrow` , `lf` , `newPage` , `space` , `tab` .
| char | char := Character cr. ^char
Create a character from a numeric code
Send a `value:` message to the `Character` class. The argument is the numeric Unicode representation for the character.
| char | char := Character value: 67. ^char
Create a composed character
Send a `composeDiacritical:` message to a character. The argument is a diacritical character, which can be obtained by sending `diacriticalNamed:` to the `Character` class with a symbol naming the diacritical character.
| baseChar diacrit composedChar | baseChar := $a. diacrit := Character diacriticalNamed: #grave. composedChar := baseChar composeDiacritical: diacrit. ^composedChar
Create a string literal
Enclose the desired characters in single quotes.
| string | string := 'This is a string.'. ^string
Create an empty string
Send a `new` message to the `String` class. This is equivalent to enclosing nothing between single quotes.
| emptyString | emptyString := String new. ^emptyString
Create a string of a set size
Send a `new:` message to the `String` class with an integer specifying the string length.
By default, each character is a null. To supply another character, send a `new:withAll:` message to the `String` class with the number of characters, and the fill character as the second argument.
| nullString zeroString |

nullString := String new: 10. zeroString := String new: 10 withAll: $0. ^Array with: nullString with: zeroString
Create a string from a character
Send a `with:` message to the `String` class. The argument is the character that is to be the sole element of the string.
| oneCharString | oneCharString := String with: Character tab. ^oneCharString
Test for character attributes
Messages are provided in the Character class to test whether a character is alphabetic (English a-z or A-Z), alphanumeric (a-z, A-Z, or 0-9), a letter (a-z, A-Z, or in a set of non-English letters), and a vowel.
| char responses | char := $a. responses := Array new: 4.

responses at: 1 put: char isAlphabetic; at: 2 put: char isAlphaNumeric; at: 3 put: char isLetter; at: 4 put: char isVowel. ^responses
Test if a character is a digit
Send an `isDigit` message to a character. The response is `true` when the character is in the range 0-9.
| char | char := $5. ^char isDigit
Test for upper- and lower-case
Send an `isLowercase` message to the character. The response is `true` when the character is a lowercase letter.
Send an `isUppercase` message to find out whether the character is an uppercase letter.
| char isLower isUpper | char := $C.

isLower := char isLowercase. isUpper := char isUppercase.

^Array with: isLower with: isUpper.
Test for white space
Send an `isSeparator` message to the character. The response is `true` when the character is a space, tab, carriage return, line feed, form feed, or null.
| char | char := Character cr. ^char isSeparator
Test for composed character
Send an `isComposed` message to the character. The response is `true` when the character is composed of a base character plus a diacritical mark.
To find out whether a character is a diacritical mark (alone), send an `isDiacritical` message.
| char | char := Character diacriticalNamed: #grave. ^Array with: char isComposed with: char isDiacritical
Change character case
To convert a string to all lowercase letters, send an `asLowercase` message to the string. To convert a string to all uppercase, send an `asUppercase` message.
| string | string := 'North American Fertilizer Company'.

^string asUppercase
To change the case of a selected character, send an `asUppercase` or `asLowercase` message to the character.
| string prevCharIsSeparator newChar | string := 'NORTH AMERICAN FERTILIZER COMPANY'. prevCharIsSeparator := true.

string keysAndValuesDo: [ :index :char | prevCharIsSeparator ifTrue: [newChar := char asUppercase] ifFalse: [newChar := char asLowercase]. string at: index put: newChar. prevCharIsSeparator := char isSeparator].

^string
Get a string's length
Send a `size` message to the string.
| string | string := '123456789'. ^string size
Get a string's width in pixels
Send a `widthOfString:` message to the graphics context of the display surface on which the string will be displayed. The argument is the string. The width in pixels is returned.
| window string width | window := ScheduledWindow new. string := 'Hello, world'.

width := window graphicsContext widthOfString: string. ^width
Compare characters and strings
Characters and strings respond to the same comparison messages as most objects: `=` , `==` , `<` , `>` , and so on.
Characters are compared based on their numeric equivalents.
char1 = char2 "equality"
To compare based on identity, send an `==` or `~~` (not identical) message to the object. Two different strings cannot be identical, though two variables that refer to the same string are identical.
| str1 str2 str3 | str1 := 'Excellent'. str2 := 'Excellent'. str3 := str1.

^Array with: (str1 == str2) with: (str1 == str3)
Compare by sorting order
Send a `<` , `>` , `<=` , or `>=` message to the object.
In most cases, ordering is determined by alphabetical order, without regarding case. However, if two strings have the same letters but differ by case, their order is determined by the numeric value of the characters.
| str1 str2 str3 |

str1 := 'north'. str2 := 'North'. str3 := 'northwest'.

^Array with: (str1 < str2) with: (str2 < str1) with: (str1 < str3) with: (str2 < str3)
Compare strings ignoring case
Send a `sameAs:` message to one of the strings. The argument is the second string.
| str1 str2 str3 | str1 := 'north'. str2 := 'North'. str3 := 'northwest'.

^Array with: (str1 sameAs: str2) with: (str1 sameAs: str3) with: (str2 sameAs: str3)
Rate the similarity of two strings
Send a `sameCharacters:` message to one of the strings. The argument is the second string. An integer is returned, indicating how many of the beginning characters are the same (including case) in the two strings.
Send a `spellAgainst:` message to one of the strings, with the second string as argument. An integer from 1 (entirely different) through 100 (equal) is returned.
| str1 str2 str3 | str1 := 'north'. str2 := 'North'. str3 := 'northwest'.

^Array with: (str1 sameCharacters: str2) with: (str1 sameCharacters: str3) with: (str1 spellAgainst: str2) with: (str1 spellAgainst: str3)
Search strings
To get the index of a character, send an `indexOf:` message to the string. The argument is the search character. If it is not found, zero is returned.
To find the starting index of a substring, send a `findString:startingAt:ifAbsent:` message to the string. The first argument is the substring to be found. The second argument is the character position at which the search is to begin. The third argument is a block containing actions to be taken if the substring is not found (often an empty block, to avoid the default error).
| classComment searchChar searchString index1 index2 | classComment := String comment. searchChar := $<. searchString := 'Class Variables:'.

index1 := classComment indexOf: searchChar. index2 := classComment findString: searchString startingAt: 1 ifAbsent: [ ].

^Array with: index1 with: index2
Search while ignoring case
Send a `findString:ignoreCase:useWildcards:` message to the string. The `findString` argument is the substring to be found. The `ignoreCase` argument is true when case difference is to be ignored. The `useWildcards` argument is `true` when the number sign and asterisk are to be interpreted as wildcard characters rather than literal characters.
Because the presence of an asterisk wildcard affects the endpoint of the found string, this message returns an `Interval` identifying the index range of the found string. A zero interval is returned when the search string is not found.
| classComment searchString interval | classComment := String comment. searchString := 'Var*:'.

interval := classComment findString: searchString startingAt: 1 ignoreCase: true useWildcards: true.

^classComment copyFrom: interval first to: interval last
Combine two strings
In simple situations, you can combine two strings using a comma.
| firstName lastName fullName space | firstName := 'Bill'. lastName := 'Clinton'. space := String with: Character space.

fullName := firstName, space, lastName. ^fullName
Combine strings using a stream
For concatenating several strings, it is more efficient to use a stream.
Create a stream by sending an `on:` message to the `WriteStream` class. The argument is typically an empty string, but it could be any string, such as a preassembled report heading.
Append each string in the series to the stream by sending a `nextPutAll:` message to the stream, with the string as argument.
Get the stream contents in the form of a string by sending a `contents` message to the stream.
| classNames formalList | classNames := Smalltalk classNames. formalList := WriteStream on: String new. classNames do: [ :name | formalList nextPutAll: 'Class: '; nextPutAll: name; cr].

^formalList contents
Extract a substring
When a string contains two or more parts, getting the parts as separate strings is a common requirement.
Send a `copyFrom:to:` message to the string. The first argument is the starting index and the second argument is the ending index of the desired substring.
| fullName firstName lastName spaceIndex | fullName := 'Mahatma Gandhi'. spaceIndex := fullName indexOf: Character space.

firstName := fullName copyFrom: 1 to: spaceIndex - 1. lastName := fullName copyFrom: spaceIndex + 1 to: fullName size.

^Array with: firstName with: lastName
Copy a prefix
Send a `copyUpTo:` message to the string. The argument is the character that marks the end of the prefix (but is not included in it).
| fullName firstName | fullName := 'Boris Yeltsin'.

firstName := fullName copyUpTo: Character space. ^firstName
Remove or replace a substring
Removing characters is accomplished by creating a copy in which the unwanted characters have been replaced by an empty string.
Send a `copyReplaceFrom:to:with:` message to the string. The first and second arguments are the index locations of the starting and stopping characters in the substring that is to be replaced. The `with:` argument is the new substring, which need not be the same size as the original substring.
To insert a substring without removing any characters in the existing string, make the ending index less than the starting index.
To remove characters, replace them with an empty string.
| colorNames magentaStart yellowStart | colorNames := 'cyan magenta yellow'. magentaStart := colorNames findString: 'magenta' startingAt: 1.

"Replace magenta with oddDarkReddishColor." colorNames := colorNames copyReplaceFrom: magentaStart to: magentaStart + 'magenta' size - 1 with: 'oddDarkReddishColor'.

"Insert newColor before oddDarkReddishColor." colorNames := colorNames copyReplaceFrom: magentaStart to: magentaStart - 1 with: 'newColor '.

"Remove yellow." yellowStart := colorNames findString: 'yellow' startingAt: 1. colorNames := colorNames copyReplaceFrom: yellowStart to: yellowStart + 'yellow' size - 1 with: String new.

^colorNames
Replace multiple occurrences of a substring
When a string contains multiple occurrences of a substring, you can replace all occurrences by using this technique.
Send a `copyReplaceAll:with:` message to the string. The first argument is the substring that is to be replaced. The second argument is the replacement substring.
| colorNames | colorNames := String new. ColorValue constantNames do: [ :name | colorNames := colorNames, name asString, ' '].

colorNames := colorNames copyReplaceAll: 'Gray' with: 'Grey'. ^colorNames
Abbreviate a string by ellipsis
For long strings, you can display the beginning and ending of the string, inserting an ellipsis (...) in place of the missing characters.
Send a `contractTo:` message to the string. The argument is the number of characters in the abbreviation, including three for the ellipsis. Half of the abbreviation will be taken from the beginning of the string and the other half from the end.
| string contractedString | string := 'North American Free Trade Agreement'.

contractedString := string contractTo: 15. ^contractedString
Abbreviate by removing all vowels
Send a `dropFinalVowels` message to the string. An abbreviated string will be returned, in which only the leading vowel (if any) remains.
| string noVowelString | string := 'North American Free Trade Agreement'. noVowelString := string dropFinalVowels. ^noVowelString
Insert line-end characters
In Smalltalk methods, certain conventions of indentation and line wrapping make the code more readable. Sometimes a string disrupts the readability of the code because it contains embedded carriage returns. This technique allows you to keep the entire string on one line without sacrificing the embedded returns.
For each embedded carriage return in the string, substitute a backslash character (\).
Send a `withCRs` message to the string to convert the backslashes back to carriage returns.
Dialog request: 'This string\has 3 lines\when displayed.' withCRs initialAnswer: 'No response needed'.
Text and Fonts
Create a text object
A `ComposedText` is graphical object consisting of a string and a set of display attributes, such as boldness, font, and color. Use a composed text to format a text's appearance.
To create a composed text object, send an `asComposedText` message to a string.
| string txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. string := ComposedText comment.

txt := string asComposedText.

txt displayOn: gc at: 5@5.
Set text display attributes
Create an instance of `Text` , typically by sending an `asText` message to the string that is the basis for the composed text.
Create a `TextAttributes` , or get one from the dictionary that is held by the `TextAttributes` class by sending a `styleNamed:` message to `TextAttributes` .
Send a `withText:style:` message to the `ComposedText` class. The first argument is the text. The second argument is the `TextAttributes` .
| txt gc textStyle | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := ComposedText comment asText. textStyle := TextAttributes styleNamed: #large.

txt := ComposedText withText: txt style: textStyle.

txt displayOn: gc at:5@5.
Get available text styles
Instances of `TextAttributes` , `VariableTextAttributes` , or its subclasses are used to define and share text styles throughout VisualWorks. The most common of these instances are available from a class dictionary in `TextAttributes` . The dictionary keys of these text styles may be determined by sending the message `styles` to `TextAttributes` . The answer is a set with a symbol for each style in the dictionary.
^TextAttributes styles
To access a text style, send the message `styleNamed:` to the class `TextAttributes` and provide the text style name symbol as argument.
^TextAttributes styleNamed: #large
Text style selections are identified in the VisualWorks Settings Tool at its Default Font page and in the UI Painter Properties Tool for any widget that displays text. These selections correspond to the following text style symbols in the `TextAttributes` dictionary. Note that `#default` , `#large` , `#systemDefault` , `#small` , and `#fixed` are a scaled text styles (i.e. instances of `VariableSizeTextAttributes` ).
Properties Tool Font Style Settings Tool - Default Font

Default #default Medium Constant Large #pixelLarge Constant Medium #pixelDefault Constant Small #pixelSmall Constant Fixed #pixelFixed Scaled Large #large Large Scaled Medium #systemDefault Scaled Small #small Small Scaled Fixed #fixed Fixed
Display a text object
A `ComposedText` is a visual component, so you can display it on a display surface. The example shows how to display on a graphics context, but more usually you display it on a textual widget using its access procedures.
Get the graphics context from the display surface by sending a `graphicsContext` message.
Send a `displayOn:` message to the composed text. The argument is the graphics context of the display surface.
| txt gc | txt := ComposedText comment asComposedText. gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt displayOn: gc at: 5@5.
Set the text line length
When a composed text is displayed in a text widget, its width is adjusted automatically by the widget. This example shows how to set the line length if the text is displayed directly on a scratch window.
Send a `compositionWidth:` message to the composed text. The argument is the line length in pixels.
To get the current line length, send a `compositionWidth` message.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := VisualComponent comment asComposedText.

txt compositionWidth: 380.

txt displayOn: gc at: 5@5.
Disable word wrapping
Word-wrapping for text can be disabled for columnar material or other text that would be disrupted by wrapping. This only works when displaying a text directly on a display surface. Text widgets do not consult the text about word wrapping, so you must turn off word wrapping in the text widget itself.
Send a `wordWrap:` message to the composed text. The argument is `false` to disable wrapping and `true` to turn it on.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := ComposedText comment asComposedText. txt compositionWidth: 380.

txt wordWrap: false.

txt displayOn: gc at: 5@5.
Set text alignment
By default, a composed text starts each new line flush against the left margin. The alignment can be changed for the text.
For flush-left text (the default), send a `leftFlush` message to the composed text.
For flush-right text, send `rightFlush` .
For centered text, send `centered` .
For text that aligns with both left and right margins, send `justified` .
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := VisualComponent comment asComposedText. txt compositionWidth: 380.

txt rightFlush.

txt displayOn: gc at: 5@5.
Set text indents
Send a `firstIndent:` message to the composed text. The argument is the width in pixels of the first line's indentation from the left edge.
To set the indent for later lines, send a `restIndent:` message to the composed text. The argument is the width of the indentation from the left edge for all lines after the first line.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'Line 1\Line 2\Line 3\Line 4' withCRs asComposedText. txt compositionWidth: 380.

txt firstIndent: 50. txt restIndent: 100.

txt displayOn: gc at: 5@5.
Set tab stops
Get a copy of the `TextAttributes` from the composed text by sending a `textStyle` message followed by a `copy` message.
Send a `useTabs:` message to the text style. The argument is an array containing one or more tab settings. Each setting is an integer indicating how many pixels separate that tab stop from the `restIndent` setting. When each tab is an equal distance from its predecessor, the array can contain a single integer indicating that separation distance.
Install the modified text style in the composed text by sending a `textStyle:` message to the text, with the style as the argument.
| txt gc style tab | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. tab := String with: Character tab. txt := ('Line 1\Line 2\Line 3\', tab, '1 Tab\', tab, tab, '2 Tabs\', tab, tab, tab, '3 Tabs'). txt := txt withCRs asComposedText. txt compositionWidth: 380.

txt firstIndent: 50. txt restIndent: 100.

style := txt textStyle copy. style useTabs: #( 15 ). txt textStyle: style.

txt displayOn: gc at: 5@5.
Get text size in characters
A `ComposedText` holds a `Text` , which in turn holds a `String` . Like a `String` , a `Text` can supply its size, measured in characters. When you need to know how many characters a `ComposedText` contains, the basic steps show how to query the underlying `Text` .
Get the underlying `Text` object from the composed text by sending a `text` message.
Send a `size` message to the `Text` .
| composedText plainText | composedText := Object comment asComposedText.

plainText := composedText text.

^plainText size
Print a text object
A composed text can be printed on the default printer by sending a `hardcopy` message to the text.
Send a `hardcopy` message to a composed text.
| txt | txt := Object comment asComposedText.

txt hardcopy.
Find/replace in text
A `ComposedText` has a `Text` , which has a `String` . The normal string search and replace techniques for strings are used on the embedded string.
Get the string from the composed text by sending a `string` message to the text.
Send a `findString:startingAt:ignoreCase:useWildcards:` message to the string. The `findString` argument is the substring to be found. The `startingAt` argument is the index position at which the search is to begin. The `ignoreCase` argument is `true` when case difference is to be disregarded. The `useWildcards` argument is `true` when the pound sign (`#` ) and asterisk (`*` ) are to be treated as wildcard characters, with the pound sign taking the place of any single character and the asterisk taking the place of zero or more characters.
| composedText string | composedText := Object comment asComposedText.

string := composedText string.

^string findString: 'Var*:' startingAt: 1 ignoreCase: true useWildcards: true.
Compare text objects
A `ComposedText` can only tell whether it is the same object as another text. The equality (`=` ) comparison has the same effect as an identity (`==` ) comparison. For more detailed comparisons, test the underlying `Text` objects, which compare their underlying strings.
To test whether two variables reference the same `ComposedText` object, send an `=` message to one variable, with the second variable as the argument.
To test whether two different instances of `ComposedText` have equal `Text` objects and hence equal strings, get the text from each composed text and compare using an `=` message.
| txt1 txt2 equal equivalent | txt1 := Object comment asComposedText. txt2 := Object comment asComposedText.

equal := txt1 = txt2. equivalent := txt1 text = txt2 text.

^Array with: equal with: equivalent
Change case
The underlying `Text` that is held by a composed text can be converted to uppercase or lowercase.
Get the underlying `Text` from the composed text by sending a `text` message, and then change the case by sending either an `asUppercase` or `asLowercase` message.
Install the changed text by sending a `text:` message to the composed text. The argument is the changed text.
| composedText capText gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. composedText := 'Hello, World' asComposedText.

capText := composedText text asUppercase. composedText text: capText.

composedText displayOn: gc at: 5@5.
Apply character formats
A composed text has a `TextAttributes` object, which specifies the display characteristics of each character in the text. A default set of text attributes are used unless you supply an alternate.
Send an `emphasizeFrom:to:with:` message to a `Text` . The first and second arguments identify the character range to be modified. The third argument is the emphasis value. Standard emphases are `#bold` , `#italic` , `#serif` , `#underline` , `#strikeout` , `#large` , and `#small` .
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'normal bold italic serif underline strikeout large small' asText.

txt emphasizeFrom: 8 to: 11 with: #bold. txt emphasizeFrom: 13 to: 18 with: #italic. txt emphasizeFrom: 20 to: 24 with: #serif. txt emphasizeFrom: 26 to: 34 with: #underline. txt emphasizeFrom: 36 to: 44 with: #strikeout. txt emphasizeFrom: 46 to: 50 with: #large. txt emphasizeFrom: 52 to: 56 with: #small.

txt displayOn: gc at: 5@25.
To apply multiple attributes to a range of characters, such as bold and italic, an array containing the emphases is be used.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'normal bold&italic large&bold&italic&underline' asText.

txt emphasizeFrom: 8 to: 18 with: #( #bold #italic). txt emphasizeFrom: 20 to: txt size with: #( #large #bold #italic #underline).

txt displayOn: gc at: 5@25.
When an entire text is to be given the same emphasis, you can send an `emphasizeAllWith:` message to the `Text` . The argument is the emphasis value or an array containing multiple emphasis values:
txt emphasizeAllWith: #( #bold #italic).
Because boldfacing an entire text is a common operation, a convenient means of applying the `#bold` emphasis to a text is provided. Send an `allBold` message to the `Text` .
txt allBold displayOn: gc at: 5@25.
Color and typeface family emphases may also be specified for text using a text style with the default set of text attributes.
Apply HTML text formats
The default set of text attributes also includes text emphases that emulate text formatting tags in HTML. Simply use the HTML tag name as the emphasis symbol over the desired range of text.
Send an `emphasizeFrom:to:with:` message to a `Text` . The first and second arguments identify the range of characters to be affected. The third argument is the emphasis value. Standard text emphases that emulate the HTML tag formats are `#H1` through `#H6` , `#STRONG` , `#CITE` , `#CODE` , `#EM` , `#VAR` , `#KBD` , `#SAMP` , and `#TT` . Many of these HTML tags have equivalents.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

txt := 'H1 H2 H3 H4 H5 H6 STRONG CITE CODE' asText. txt emphasizeFrom: 1 to: 2 with: #H1. "Level 1 Heading" txt emphasizeFrom: 4 to: 5 with: #H2. "Level 2 Heading" txt emphasizeFrom: 7 to: 8 with: #H3. "Level 3 Heading" txt emphasizeFrom: 10 to: 11 with: #H4. "Level 4 Heading" txt emphasizeFrom: 13 to: 14 with: #H5. "Level 5 Heading" txt emphasizeFrom: 16 to: 17 with: #H6. "Level 6 Heading" txt emphasizeFrom: 19 to: 24 with: #STRONG. "Bold" txt emphasizeFrom: 26 to: 29 with: #CITE. "Citation" txt emphasizeFrom: 31 to: 33 with: #CODE. "Fixed width" txt displayOn: gc at: 5@25.

txt := 'EM VAR KBD SAMP TT' asText. txt emphasizeFrom: 1 to: 2 with: #EM. "Emphasis" txt emphasizeFrom: 4 to: 6 with: #VAR. "Variable name" txt emphasizeFrom: 8 to: 10 with: #KBD. "Typewriter" txt emphasizeFrom: 12 to: 15 with: #SAMP. "Literal" txt emphasizeFrom: 17 to: 18 with: #TT. "Teletype" txt displayOn: gc at: 5@50.
Set font size
Two of the standard text emphases, `#small` and `#large` , give you control over the font size within a narrow range.
Send an `emphasizeFrom:to:with:` message to the composed text's underlying `Text` . The first and second arguments define the character range by specifying the starting and stopping indexes. The third argument is `#small` or `#large` , depending on whether you want the font size to be slightly smaller or slightly larger than normal. The actual size depends on the fonts available from the operating system, and on some platforms it may not differ at all.
To return to the default size, apply a `nil` emphasis to the text.
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'large small' asText.

txt emphasizeFrom: 1 to: 5 with: #large. txt emphasizeFrom: 7 to: 11 with: #small. txt displayOn: gc at: 5@25.

txt emphasizeAllWith: nil. txt displayOn: gc at: 5@40.
Set text color
The default text style that supports standard emphasis values for text objects also supports color and patterns.
Send an `emphasizeFrom:to:with:` message to the underlying `Text` of a composed text. The first and second arguments identify the range of characters to be affected. The third argument is an association, which is created by sending a `->` message to the lookup key (`#color` ), with the desired color as the argument.
| txt gc boldBlue | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'BLACK RED GRAY BOLDBLUE' asText.

txt emphasizeFrom: 7 to: 9 with: #color -> ColorValue red. txt emphasizeFrom: 11 to: 14 with: #color -> ColorValue gray.

boldBlue := Array with: #bold with: #color -> ColorValue blue. txt emphasizeFrom: 16 to: 23 with: boldBlue. txt displayOn: gc at: 5@25.
Set text typeface family
The default font belongs to Helvetica, Arial, or a similar font family, depending on the operating system. For other fonts a text emphasis attribute may be set to choose a font from a another typeface family. This emphasis is available for any text style set for default character attributes.
Send an `emphasizeFrom:to:with:` message to the underlying `Text` of a `ComposedText` . The first and second arguments identify the range of characters to be affected. The third argument is an association, which is created by sending a `->` message to the lookup key (`#family` ), with the name string or an array of name strings for the font family to use. The name may include the wildcard character '`*` ' to match a family name with a partial description. The font available for the family that matches closest to the name offered in the argument will be used. If an array of family names is specified the first matching font family in the array will be used. If no font is available from any family name specified a font from the text style's default font family is used.
There are also two predefined single key text emphases that offer some control over the choice of font family: `#serif` (for a serif font such as Times) and `#sansSerif` (for a sans serif font such as Helvetica).
| txt gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'normal courier times helvetica terminal foobar' asText.

txt emphasizeFrom: 8 to: 14 with: #family->'courier'. txt emphasizeFrom: 16 to: 20 with: #family->'times*'. "Matches Times New Roman or Times" txt emphasizeFrom: 22 to: 30 with: #family->'helvetica'. txt emphasizeFrom: 32 to: 39 with: #family->'terminal'. "Use the default font for a family unknown to the installation" txt emphasizeFrom: 40 to: 45 with: #family->'foobar'.

txt displayOn: gc at: 5@25.
Create a text style with new emphases
A composed text's `TextAttributes` holds an instance of `CharacterAttributes` , which defines the emphases that are available to the text. Associated with each emphasis symbol is a block that operates on a `FontDescription` , held by the `CharacterAttributes` , which specifies the font size, family, boldness, and so on.
Note that a composed text applies the same line spacing to its entire text, so mixing font sizes is effective within only a narrow range for each composed text. Similarly, only a single alignment, indent, and tab stop setting remain in effect for a composed text. Separate instances of `ComposedText` are recommended in such situations.
Create a new instance of `CharacterAttributes` by sending a `newWithDefaultAttributes` message to the `CharacterAttributes` class. This message initializes the `CharacterAttributes` with the standard emphases such as `#bold` and `#italic` , so you don't have to redefine them.
Install an instance of `FontDescription` in the new `CharacterAttributes` by sending a `setDefaultQuery:` message. The argument can be either a new instance of `FontDescription` or, as in the example, a copy of the default font description from an existing text style's character attributes. The advantage of copying an existing font description is that you retain the existing settings.
Customize the `CharacterAttributes` as desired. Define a new emphasis by sending an `at:put:` message to the character attributes. The first argument is the name of the emphasis (`#title` ). The second argument is a block that sends a `pixelSize:` message to the block argument, with the desired size of the font (in pixels, not in points). The example defines a new emphasis called `#title` , which specifies that the font must be 24 pixels in height.
Create a new `TextAttributes` by sending a `characterAttributes:` message to the `TextAttributes` class. The argument is the `CharacterAttributes` that you customized in step 3.
If you intend to display unusually large or small text, as in the example, adjust the line spacing and baseline of the text style. The line spacing is set by sending a `lineGrid:` message to the text style, with an argument at least a few pixels larger than the largest font size. To set the baseline, which is the distance between the top of the line and the imaginary line on which capital letters rest, send a `baseline:` message to the text style; the argument is the distance in pixels.
Install the custom text style by sending a `textStyle:` message to the composed text. The argument is the custom `TextAttributes` from step 5.
Apply the new emphasis to the desired portions of the composed text's underlying `Text` .
| txt gc ca ta | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := Object comment asComposedText. txt compositionWidth: 300.

"Create and install a custom text style." ca := CharacterAttributes newWithDefaultAttributes. ca setDefaultQuery: txt textStyle defaultFont. ca at: #title put: [ :fontDesc | fontDesc pixelSize: 24]. ta := TextAttributes characterAttributes: ca. ta lineGrid: 27; baseline: 18. txt textStyle: ta.

txt text emphasizeAllWith: #title. txt displayOn: gc at: 5@25.
In this example text emphases `#redlined` and `#fixed` are added to the `CharacterAttributes` instance. The `#redlined` emphasis colors text red and underlines it whereas `#fixed` selects a fixed width font. Three windows are opened to show variations of `#title` , `#redlined` , and `#fixed` emphases to its text displayed.
| displayBlock txt ca textAttr | "This block enables a window to be opened displaying its composed text argument" displayBlock := [:text | | view | view := ComposedTextView new model: text asValue; textStyle: text textStyle. ScheduledWindow new label: text; component: (LookPreferences edgeDecorator on: view); openWithExtent: 300 @ 200].

ca := CharacterAttributes newWithDefaultAttributes. ca setDefaultQuery: FontDescription new. ca at: #title put: [:fontDesc | fontDesc pixelSize: 24]. ca at: #fixed put: [:fontDesc | fontDesc fixedWidth: true]. ca at: #redlined put: [:fontDesc | fontDesc underline: true; color: ColorValue red]. textAttr := TextAttributes characterAttributes: ca. textAttr gridForFont: #title withLead: 2.

"Display a window with text emphasized in title size" txt := ('This is title font' asText emphasizeAllWith: #title) asComposedText. txt textStyle: textAttr. displayBlock value: txt.

"Change the text to display redlined and display it in a new window" txt text: ('This is red lined font' asText emphasizeAllWith: #redlined). displayBlock value: txt.

"Change the text to display redlined, bold, fixed width at title size and display it in a new window" txt text: ('This is bold red lined title fixed font' asText emphasizeAllWith: #(#redlined #bold #title #fixed)). displayBlock value: txt
Adjust line spacing and baseline
When a text style is created its line spacing and baseline may be changed using the `lineGrid:` and `baseline:` messages. These changes are often necessary to adjust spacing between lines of small or large text. The line spacing is set by sending a `lineGrid:` message to the text style, with an argument at least a few pixels larger that the largest font size. To set the baseline, which is the distance between the top of the line and the imaginary line on which capital letters rest, send a `baseline:` message to the text style; the argument is the distance in pixels.
Alternately, instead of declaring text line spacing and baseline these quantities may be setup appropriately for an given text emphasis by using the `gridForFont:withLead:` message. Send a `gridForFont:withLead:` message to the `TextAttributes` that is held by the composed text. The first argument is the name of the text emphasis (`#title` ). If this argument is `nil` then the spacing adjustments apply to text without emphasis. The second argument is the leading, which is the vertical space to be left between one line and the next-- typically zero to two pixels. This adjusts both the line spacing and the baseline to suit the font's size.
| txt gc ca ta | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := Object comment asComposedText.

"Create and install a custom text style." ca := CharacterAttributes newWithDefaultAttributes. ca setDefaultQuery: txt textStyle defaultFont. ca at: #title put: [ :fontDesc | fontDesc pixelSize: 24]. ta := TextAttributes characterAttributes: ca. ta gridForFont: #title withLead: 2. txt textStyle: ta.

txt text emphasizeAllWith: #title. txt compositionWidth: 300. txt displayOn: gc at: 5@25.
Set font preferences for a text style
A `FontDescription` is a specification for a font based on typeface family, encoding, name, size or other attributes. An attribute in a `FontDescription` may be left at or set to `nil` so that particular attribute is ignored in the search for an appropriate font. A `CharacterAttributes` or `VariableCharacterAttributes` references a `FontDescription` instance to which it may further refine according to the emphases applied in a `Text` instance. The `FontDescription` is ultimately used to search for the best platform font that matches its specification. A `FontDescription` may be so loosely specified that any font may be chosen or so tightly specified that it may find no font that satisfies its criteria.
A `FontDescription` may have the following attributes set:
name
the name for the font as a string. The wildcard pattern '`*` ' may be included to match any characters in its position. Setting the font name implies that an exact match for this font is wanted on the platform. This is often too restrictive for use between several installations.
family
a string or an array containing one or more strings. Each string names a font family or includes the wildcard pattern '`*` ' for partial matching. When an array is used a match is attempted up on the first element before succeeding elements are tried. A string containing only an asterisk is used as the final element in the array to indicate that any alternate is preferable to a `"font not found"` error.
encoding
a string for the desired font encoding
manufacturer
a string or array of strings indentifying the font manufactruer to match. A wildcard pattern may be included for partial matching. For some platforms this specification is not relevant.
pixelSize
the font size in pixels
boldness
a number between 0 and 1 for the font boldness desired. A value above 0.5 implies the font should appear bold.
serif
true
if a serif font should be used.
color
the color the font should appear in other than black.
fixedWidth
true
if a fixed width font should be used (e.g. Courier).
italic
true
for an italic font.
strikeout
true
if a line should be drawn through the text.
underline
true
to underline the font.
Create a new instance of `FontDescription` and set the preferences desired. Alternately, the `FontDescription` may be modified from a copy obtained from the default text style as in
defaultFontDesc := TextAttributes default defaultFont copy.
Create a `TextAttributes` with default text attributes by sending the message `defaultFontQuery:` to it. Use the `FontDescription` created earlier as its argument. If you wish to create a text style with custom text attributes then install the `FontDescription` to the `CharacterAttributes` instance with the message `setDefaultQuery:` then create the `TextAttributes` instance by passing the message `characterAttributes:` with the `CharacterAttributes` instance.
The example shows how new text styles for `ComposedText` instances may be created by specifying only the `FontDescription` .
| displayBlock txt | "This block enables a window to be opened displaying its composed text argument" displayBlock := [:text| | view | view := ComposedTextView new model: text asValue; textStyle: text textStyle. ScheduledWindow new label: text; component: (LookPreferences edgeDecorator on: view); openWithExtent: 300@200].

"Display a window with text emphasized 24 pixels high" txt := 'This is title font' asComposedText. txt textStyle: (TextAttributes defaultFontQuery: (FontDescription new pixelSize: 24)). displayBlock value: txt.

"Display a window with text emphasized red and underlined" txt := 'This is red lined font' asComposedText. txt textStyle: (TextAttributes defaultFontQuery: (FontDescription new underline: true; color: ColorValue red)). displayBlock value: txt.

"Display red lined, bold, and fixed width text in 24 pixel size in a new window" txt := 'This is red lined bold title serif font' asComposedText. txt textStyle: (TextAttributes defaultFontQuery: (FontDescription new pixelSize: 24; boldness: 0.8; color: ColorValue red; underline: true; fixedWidth: true)). displayBlock value: txt.
Set font family for a text style
The default font belongs to Helvetica, Arial, or a similar font family, depending on the operating system. To use a font from one or more alternate families modify the family attribute of the `FontDescription` for the text style. If you wish to change the font for a limited range of text use the `#family` text emphasis and provide a string or array of strings for the font family to use.
Create a new `FontDescription` and send a `family:` message to it. The argument is an array containing one or more strings. Each string names a font family or a wildcard pattern for partial matching. A string containing an asterisk is frequently used as the final element in the array to indicate that any alternate is preferable to a `"font not found"` error.
Create a text style by sending a `defaultFontQuery:` message to the `TextAttributes` class. The argument is the `FontDescription` from step 2.
Adjust the line spacing to suit the font by sending a `gridForFont:withLead:` message to the text style. The first argument is `nil` in this case. The second argument is the amount of leading (space) between lines of text (typically zero to two pixels).
Install the text style in the composed text by sending a `textStyle:` message to the composed text. The argument is the text style from step 2.
| txt gc ta fd | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := Object comment asComposedText.

"Create and use a text style for this font." fd := FontDescription new family: #( 'bookman' 'times*' '*' ); serif: true; fixedWidth: false; pixelSize: 14. ta := TextAttributes defaultFontQuery: fd. ta gridForFont: nil withLead: 2. txt textStyle: ta.

txt compositionWidth: 300. txt displayOn: gc at: 5@25.
Set font name for a text style
Create a new `FontDescription` and send a `name:` message to it. The argument is a string that names a font family or a wildcard pattern for partial matching. (The example takes the list of available fonts from the operating system and uses the first one.)
| txt gc ca ta fd | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := Object comment asComposedText.

"Create and install a custom text style." fd := FontDescription new name: (Screen default listFontNames at: 1). ta := TextAttributes defaultFontQuery: fd. ta gridForFont: nil withLead: 2. txt textStyle: ta.

txt compositionWidth: 300. txt displayOn: gc at: 5@25.
Set font pixel size
Create a new `FontDescription` or access an existing one and send a `pixelSize:` message to it. The argument is the desired font size in pixels.
On platforms such as MS Windows or for PostScript printers font size is usually measured in points. On MS Windows, the font pixel size equivalent to a given point size is given by the following relationship for most VGA or better screen resolutions:
pixelSize := (pointSize * (96/72) asFloat) rounded.
The example below creates a text style for a 22 pixel font given the default font preferences.
| txt gc ca ta fd | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := Object comment asComposedText.

"Copy the default font description and set its pixel size to 22" fd := txt textStyle defaultFont copy. fd pixelSize: 22. ta := TextAttributes defaultFontQuery: fd. ta gridForFont: nil withLead: 2. txt textStyle: ta.

txt compositionWidth: 300. txt displayOn: gc at: 5@25.
If the text style is scaled (i.e. the text style is an instance of `VariableSizeTextAttributes` ) then changing the pixel size of its FontDescription will have no effect in text size. Scaled text styles need to be rescaled instead. Text styles `#default` , `#small` , `#large` , `#systemDefault` , and `#fixed` are scaled. Send the message `scalingFactor:` to the scaled text style. The argument is a ratio of the desired pixel size to the preferred pixel size.
Create a scaled text style
When text is displayed on different screen sizes or resolutions often there is a need to resize text for better visibility. A text style based on an instance of `VariableSizeTextAttributes` permits composed text to be scaled relative to a single preferred font pixel size set for the VisualWorks `Locale` . A `VariableCharacterAttributes` instance is used with a `VariableSizeTextAttributes` to define the emphases and scaling applied to a composed text. Instances of `VariableSizeTextAttributes` and `VariableCharacterAttributes` will work in place of `TextAttributes` and `CharacterAttributes` instances; all respond to the same methods to define emphases and format text. The pixel size attribute for a `FontDescription` installed in a `VariableCharacterAttributes` instance is not used however. Instead, the `scalingFactor:` message to either a `VariableSizeTextAttributes` or `VariableCharacterAttributes` instance determines text size.
Create a new text style from `VariableSizeTextAttributes` by sending its class the message `defaultFontQuery:` with a `FontDescription` . The example uses the `FontDescription` from the default text style. Alternately, an instance of `VariableSizeTextAttributes` may be created by sending the message `characterAttributes:` with an instance of `VariableCharacterAttributes` .
To scale the text style either larger or smaller than the preferred pixel size send the message `scalingFactor:` to the text style from step 1. The argument is a ratio of the desired pixel size to the preferred pixel size. For example, if the preferred pixel size is 16 a scaling of 1.5 displays the font at a pixel size of 24.
Install the text style in the composed text by sending a `textStyle:` message to the composed text. The argument is the text style from step 2.
| gc fd largeScaledStyle smallScaledStyle txt | gc := ExamplesBrowser prepareScratchWindow graphicsContext.

fd := TextAttributes default defaultFont. "Create scaled text styles" smallScaledStyle := VariableSizeTextAttributes defaultFontQuery: fd. smallScaledStyle scalingFactor: 0.5. largeScaledStyle := VariableSizeTextAttributes defaultFontQuery: fd. largeScaledStyle scalingFactor: 2.

"Display text one half the preferred pixel size" txt:= ComposedText withText: 'This text is scaled small' style: smallScaledStyle. txt displayOn: gc at: 5@25.

"Display text normal size" txt:= 'This text is scaled normal' asComposedText. txt displayOn: gc at: 5@50.

"Display text twice the preferred pixel size" txt:= ComposedText withText: 'This text is scaled large' style: largeScaledStyle. txt displayOn: gc at: 5@75.
Add a text style to the font selection menu
All widgets that display text have a selection of text styles available in a Property Tool when they are under edit in the UI Painter canvas. You can expand the menu to include a custom text style.
Create the desired text style. In the example, a 24-pixel font is created.
Install the text style in the system's dictionary of styles by sending a `styleNamed:put:` message to the `TextAttributes` class. The first argument is a lookup name, specified as a `Symbol` --a capitalized version of the name will appear in the fonts menu. The second argument is the custom text style.
| fd ta | fd := FontDescription new pixelSize: 24. ta := TextAttributes defaultFontQuery: fd. ta gridForFont: fd withLead: 2.

TextAttributes styleNamed: #title put: ta.
Set preferred font family
Each text style that displays a different typeface will use a `FontDescription` with a font family list different by either name, number, or order (without use of emphasis). There is a way to override this and have all text styles choose first a font from a common list of one or more preferred font families. If no font is found from this family preference list then the text style searches its own list of font families.
Access the current `Locale` and send the message `preferredFontFamily:` to it. For the argument provide an array with one or more font family name strings. The wildcard character '`*` ' may be used in a name for partial matching. The example shown below sets the preferred font family to be `'system'` or, if that is not available, then `'gill'` .
Refresh the windows that are already open by sending a `resetViews` message to the `TextAttributes` class. When they are redisplayed, they will use the new family preferences.
Locale current preferredFontFamily: #('system' 'gill'). TextAttributes resetViews.
Normally the family preference list is empty. To clear family preferences set the list to be empty.
Locale current preferredFontFamily: #().
To determine what the current font family preferences are, if any, send the accessor message `preferredFontFamily` to the current `Locale` .
^Locale current preferredFontFamily
Set preferred font pixel size
When a scaled text style is used such as `#default` the font is scaled relative to the preferred pixel size set for the current `Locale` . One can globally resize all scaled text styles in VisualWorks by changing the preferred font pixel size.
Access the current Locale and send the message `preferredPixelSize:` to it. Provide a font pixel size as an integer for the argument.
Refresh the windows that are already open by sending a `resetViews` message to the `TextAttributes` class. When they are redisplayed, text using a scaled text style will be resized.
Locale current preferredPixelSize:18. TextAttributes resetViews.
To determine what the preferred font pixel size is send the accessor message `preferredPixelSize` to the current `Locale` .
^Locale current preferredPixelSize
Get platform fonts
When you develop an application for a single platform, rather than for portability to several platforms, specifying a platform-specific font directly gives you the greatest control over font selection.
Get the default `Screen` by sending a `default` message to that class.
Get the list of platform font names by sending a `listPlatformFonts` message to the default screen.
initialize platformFonts := SelectionInList with: Screen default listFontNames.

platformFonts selectionIndexHolder onChangeSend: #changedFont to: self.
Online example: Font2Example
Change the default font
The default font that is used by VisualWorks tools to display textual information can be changed as shown in the basic steps. Widgets in which the `Default` font has been selected, both in system tools and in your applications, are also affected.
Send a `setDefaultTo:` message to the `TextAttributes` class. The argument is the `Symbol` that names the desired text style. The text style must have been defined and installed in the fonts menu previously.
Refresh the windows that are already open by sending a `resetViews` message to the `TextAttributes` class. When they are redisplayed, they will use the new default.
TextAttributes setDefaultTo: #default. TextAttributes resetViews.
Apply the platform default font
The `System` font is selected to mimic the appearance of the platform font for the current UI look. A widget that uses the `System` font has the best chance of looking like other applications on any platform on which it is deployed.
The `System` text style can be applied to any composed text.
Get the look policy class as set by the UI look settings by sending a `preferenceFor:` message to the `UISettings` class, with the argument `#defaultLook` .
Obtain the system text style by sending message `systemTextStyle` to the look policy class from step 1.
Send a `textStyle:` message to the composed text. The argument is the text style from step 2.
| txt gc lookPolicyClass | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. txt := 'Hello, World' asComposedText.

lookPolicyClass := UISettings preferenceFor: #defaultLook. txt textStyle: lookPolicyClass systemTextStyle.

txt displayOn: gc at: 5@25.
Text Files
Create a disk file
Send an `asFilename` message to the string identifying the desired file or directory. The disk file or directory is not affected by the mere creation of a `Filename` object. Because no explicit link exists to the disk file or directory, you need not do anything explicit to release the external resource when you are finished with it.
| name filename | name := 'test.tmp'. filename := name asFilename. ^filename
Create a disk directory
Send a `makeDirectory` message to the `Filename` representing the desired directory. If the disk directory already exists, an error results.
| directory | directory := 'test' asFilename. directory makeDirectory. ^directory exists
Construct a portable file name
Send a `construct:` message to the `Filename` representing the parent directory. When a pathname is to represent a hierarchy of nested parent directories, use a series of such `construct:` messages.
| unixDir portableDir | unixDir := 'visual/utils' asFilename.

portableDir := 'visual' asFilename construct: 'utils'.

unixDir inspect. portableDir inspect.
Check whether a file or directory exists
Send an `exists` message to the `Filename` . If the disk file or directory exists, `true` is returned.
| unlikelyFile | unlikelyFile := 'qqqqzzzz' asFilename. ^unlikelyFile exists
Get the working directory
Send a `defaultDirectory` message to the `Filename` class. A `Filename` representing the working directory is returned.
| workingDir | workingDir := Filename defaultDirectory. ^workingDir
Get the parent directory
Send a `directory` message to the `Filename` . A `Filename` representing the parent directory is returned.
| dir parentDir | dir := Filename defaultDirectory.

parentDir := dir directory. ^parentDir
Get the parts of a pathname
To get the entire pathname as a string, send an `asString` message to the `Filename` .
To get the directory part of a pathname, send a `head` message to the `Filename` . A string containing the directory's pathname is returned.
To get the file part of the pathname, send a `tail` message. A string containing the file's name is returned.
| filename pathString dirString fileString | filename := Filename defaultDirectory.

pathString := filename asString. dirString := filename head. fileString := filename tail.

^' PATH: ', pathString, ' DIRECTORY: ', dirString, ' FILE: ', fileString
Distinguish a file from a directory
Send an `isDirectory` message to the `Filename` . If the `Filename` represents a disk directory, `true` is returned. If it represents a disk file, `false` is returned. If neither a file nor a directory with a matching name exists, an error results.
| dir | dir := Filename defaultDirectory. ^dir isDirectory
Get the access and modification times
Get a dictionary containing dates and times associated with a file or directory by sending a `dates` message to the `Filename` .
Get the desired date-time pair by sending an `at:` message to the dictionary. The argument is `#accessed` for the time at which the file's contents were most recently accessed. The argument is `#modified` for the time of the most recent modification to the file's contents. The argument is `#statusChanged` for the time of the most recent change in external attributes of the file, such as ownership and permissions.
If the operating system does not support the requested type of information, nil is returned; otherwise, an array containing a date and a time is returned.
| newFile stream datesDict modifyDates modifyDate modifyTime | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close. datesDict := newFile dates. modifyDates := datesDict at: #modified. modifyDates isNil ifFalse: [ modifyDate := modifyDates first. modifyTime := modifyDates last]. ^'
Get the size of a file
Send a `fileSize` message to the `Filename` . If the file exists, the number of characters it contains is returned. If the file does not exist, an error results. If the `Filename` represents a disk directory rather than a disk file, zero is returned.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

^newFile fileSize.
Get the contents of a file
Send a `contentsOfEntireFile` message to a `Filename` representing a disk file. A string is returned.
| newFile stream contents | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

contents := newFile contentsOfEntireFile. ^contents
Get the contents of a directory
Send a `directoryContents` message to a `Filename` representing a disk directory. An array of file and subdirectory names is returned.
| workingDir contents | workingDir := Filename defaultDirectory.

contents := workingDir directoryContents. ^contents
Store text in a file
Putting a string into a disk file involves using a stream. The technique illustrated erases any existing file contents.
Create a `Filename` by sending an `asFilename` message to a string containing the pathname.
Create a stream for writing characters onto the file by sending a `writeStream` message to the `Filename` .
Send the string's characters to the file by sending a `nextPutAll:` message to the stream. The argument is the string. This operation can be repeated for a series of strings.
Close the stream by sending a `close` message to it.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

^newFile contentsOfEntireFile
Append text to a file
When creating the stream, send an `appendStream` message to the `Filename` .
| filename stream | filename := 'testFile' asFilename.

"Creating the file." stream := filename writeStream. stream nextPutAll: 'FIRST STRING'. stream close.

"Appending" stream := filename appendStream. stream nextPutAll: ' -- SECOND STRING'. stream close.

^filename contentsOfEntireFile
Open an editor on a file
The VisualWorks text editor allows the user to alter the contents of a file. For read-only access to the file, create a canvas containing a read-only text editor or a text editor with a limited menu.
Send an `edit` message to the `Filename` . If the `Filename` represents a disk directory, an error results. If the `Filename` represents a nonexistent file, an editor is opened with which the user can create the contents of the file.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile edit.
Delete a file or directory
On operating systems such as UNIX that support multiple pathnames for the same physical disk file or directory, deleting as shown here removes the reference that is identified by the pathname, but it does not delete the physical file or directory if another reference exists.
If necessary, confirm that the disk file or directory to be deleted exists by sending an `exists` message to the `Filename` .
Send a `delete` message to the `Filename` .
| newFile stream pretest posttest | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close. pretest := newFile exists.

newFile delete. posttest := newFile exists.

^' EXISTS BEFORE DELETION: ', pretest printString, ' EXISTS AFTER DELETION: ', posttest printString.
Copy a file
Send a copyTo: message to the Filename. The argument is a string containing the pathname of the copy. If the Filename represents a directory or a nonexistent disk file, an error results.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile copyTo: 'testFile.tmp'.

^'testFile.tmp' asFilename exists.
Move a file
Send a `moveTo:` message to the `Filename` . The argument is a string containing the new pathname, which can include a different directory. If the `Filename` represents a directory or a nonexistent disk file, an error results.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile moveTo: 'testFile.tmp'.

^'testFile.tmp' asFilename exists.
Rename a file
Send a `renameTo:` message to the `Filename` . The argument is a string containing the new pathname, which can include a different directory. If the `Filename` represents a directory or a nonexistent disk file, an error results.
| newFile stream | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile renameTo: 'testFile2.tmp'.

^'testFile2.
Compare two filenames or files
To compare two filenames, send an `=` message to one `Filename` . The argument is the second `Filename` . If they have the same pathname (that is, they point to the same physical disk file), `true` is returned.
To compare the contents of two disk files, get the contents of each file by sending `contentsOfEntireFile` messages to the `Filenames` . Then send an `=` message to one of the resulting strings, with the other string as the argument.
| file1 file2 stream pathsAreEqual contentsAreEqual | file1 := 'fileOne' asFilename. file2 := 'fileTwo' asFilename. stream := file1 writeStream. stream nextPutAll: Object comment. stream close. file1 copyTo: file2 asString.

pathsAreEqual := ( file1 = file2). contentsAreEqual := ( file1 contentsOfEntireFile = file2 contentsOfEntireFile).

^' PATHS ARE EQUAL: ', pathsAreEqual printString, ' CONTENTS ARE EQUAL: ', contentsAreEqual printString.
Compare directories
To compare the contents of two disk directories, get the contents of each directory by sending `directoryContents` messages to the `Filenames` . Then send an `=` message to one of the resulting arrays, with the other array as the argument.
| dir1 dir2 pathsAreEqual contentsAreEqual | dir1 := Filename defaultDirectory. dir2 := dir1 directory.

pathsAreEqual := ( dir1 = dir2). contentsAreEqual := ( dir1 directoryContents = dir2 directoryContents).

^' PATHS ARE EQUAL: ', pathsAreEqual printString, ' CONTENTS ARE EQUAL: ', contentsAreEqual printString.
Print a file
Some operating systems support printing a text file directly, and others require that it first be converted to or a printer-specific format. The technique shown here prints a file regardless of the operating system, by converting the file contents to a composed text.
Get the contents of the file by sending a `contentsOfEntireFile` message to the `Filename` . Convert the resulting string to a `ComposedText` by sending an `asComposedText` message to it.
Print the composed text by sending a `hardcopy` message to it.
| newFile stream contents composedText | newFile := 'testFile' asFilename printTextFile. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

contents := newFile contentsOfEntireFile. composedText := contents asComposedText. composedText hardcopy.
Print a file directly
Send a `printTextFile` message to the `Filename` . If text file printing is not supported by the operating system, an error results.
| newFile stream | newFile := 'testFile' asFilename printTextFile. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile printTextFile
Write fields to a data file
Create a write stream on the file by sending a `writeStream` message to the `Filename` .
Create a block in which, for each field of data, a `nextPutAll:` message is sent to the stream with the data string as argument, followed by a `nextPut:` message with the separator character as argument.
Send an `ensure:` message to the data-writing block. The argument is another block that closes the stream by sending a `close` message to it.
To confirm the operation, open an editor on the data file.
| dataFile stream separator writingBlock | dataFile := 'dataFile' asFilename. separator := $,. "comma"

stream := dataFile writeStream. writingBlock := [ ColorValue constantNames do: [ :color | stream nextPutAll: color. stream nextPut: separator]]. writingBlock ensure: [stream close].

dataFile edit.
Read fields from a data file
Create a read stream on the file by sending a `readStream` message to the `Filename` .
Create a block in which the next field of data is fetched by sending an `upTo:` message to the stream, with the separator character as the argument. This is repeated by placing it within an inner block that is repeated until the end of the stream is encountered.
Send an `ensure:` message to the data-reading block. The argument is another block that closes the stream by sending a `close` message to it.
| dataFile stream separator writingBlock colorNames readingBlock | dataFile := 'dataFile' asFilename. separator := $,. "comma"

"Write data" stream := dataFile writeStream. writingBlock := [ ColorValue constantNames do: [ :color | stream nextPutAll: color. stream nextPut: separator]]. writingBlock ensure: [stream close].

"Read data" stream := dataFile readStream. colorNames := OrderedCollection new. readingBlock := [ [stream atEnd] whileFalse: [ colorNames add: (stream upTo: separator)]]. readingBlock ensure: [stream close].

^colorNames
Set file permissions
On operating systems such as UNIX that support file and directory permissions, the permission to change a file can be added or removed as shown in the basic steps.
To remove the permission to change the contents of a file or directory, send a `makeUnwritable` message to the `Filename` .
To restore the writing permission, send a `makeWritable` message.
To find out whether the writing permission is enabled, send a `canBeWritten` message. If the file or directory does not exist, a response of `true` indicates that the parent directory is writable. The `canBeWritten` test works on all operating systems.
| newFile stream removed restored | newFile := 'testFile' asFilename. stream := newFile writeStream. stream nextPutAll: Object comment. stream close.

newFile makeUnwritable. removed := newFile canBeWritten.

newFile makeWritable. restored := newFile canBeWritten.

^' PERMISSION REMOVED: ', removed printString, ' PERMISSION RESTORED: ', restored printString.
Geometrics
Display a point
Displaying a single point is occasionally needed for building a dotted pattern. This is done by displaying a dot.
Create a `Point` by sending an `@` message to the integer representing the x coordinate. The argument is the y coordinate.
Display the point by sending a `displayLineFrom:to:` message to the graphics context. The first argument is the point and the second argument is a neighboring point, which can be derived by adding 1 to the point.
| gc random points | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. random := Random new. points := OrderedCollection new.

"Create 1000 random points in a 100-pixel square." 1000 timesRepeat: [ points add: ((random next * 100) @ (random next * 100))].

"Display each random point." points do: [ :pt | gc displayLineFrom: pt to: pt + 1]
Draw a line
To draw a straight line directly on a display surface:
Get the graphics context of the display surface by sending a `graphicsContext` message.
Send a `displayLineFrom:to:` message to the graphics context. The first argument is the starting point of the line and the second argument is the endpoint.
| gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. 5 to: 400 by: 5 do: [ :i | gc displayLineFrom: 0@i to: i@400].
Create and display a line segment
Creating a `LineSegment` is useful when your application needs to perform an operation on the line, such as determining its length or scaling it.
Create a line segment by sending a `from:to:` message to the `LineSegment` class. The first argument is the starting point of the line and the second argument is the endpoint.
Perform any desired operations on the line (in the example, the x dimension is exaggerated by a factor of 10).
Wrap the line segment in a stroking wrapper by sending an `asStroker` message to it. This equips the line with the ability to render itself.
Display the wrapped line segment by sending a `displayOn:` message to its stroking wrapper. The argument is the graphics context of the display surface.
| gc line scaleFactor | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. scaleFactor := 10@1.

5 to: 400 by: 5 do: [ :i | line := LineSegment from: 0@i to: i@400. line := line scaledBy: scaleFactor. line asStroker displayOn: gc].
Create and display a polyline
A jointed, multi-segmented line, or polyline, can be instantiated and drawn as a `PolyLine` .
Create a `Polyline` by sending a `vertices:` message to the `Polyline` class. The argument is the collection of vertices. Then wrap the polyline in a stroking wrapper (using `asStroker` ) and display it on the graphics context (using `displayOn:` ).
| gc points x y radians polyline | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. points := OrderedCollection new. 0 to: 360 by: 30 do: [ :angle | radians := angle degreesToRadians. x := 200 - (200 * radians cos). y := 200 - (200 * radians sin). points add: x@y]. gc displayPolyline: points. polyline := Polyline vertices: points. 0.9 to: 0.1 by: -0.1 do: [ :scale | polyline := polyline scaledBy: scale. polyline asStroker displayOn: gc].
Create and display a curved line
A `Spline` is a smooth line that curves at each point in a collection.
Create a `Spline` by sending a `controlPoints:` message to the `Spline` class. The argument is a collection of points.
Wrap the spline in a stroking wrapper by sending an `asStroker` message to it.
Display the wrapped spline by sending a `displayOn:` message to the stroking wrapper. The argument is the graphics context.
| gc points spline random x y | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. points := OrderedCollection new. random := Random new.

"Collect 10 random points." 10 timesRepeat: [ x := random next * 400. y := random next * 400. points add: x@y. gc displayDotOfDiameter: 8 at: points last].

spline := Spline controlPoints: points. spline asStroker displayOn: gc.
Create and display a bezier curve
Create a bezier curve by sending a `start:end:controlPoint1:controlPoint2:` message to the `Bezier` class. Each of the arguments is a point.
Wrap the bezier curve in a stroking wrapper by sending an `asStroker` message to it.
Display the wrapped spline by sending a `displayOn:` message to the stroking wrapper. The argument is the graphics context.
| gc points bezier random x y | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. points := OrderedCollection new. random := Random new.

"Collect 10 random points." 4 timesRepeat: [ x := random next * 400. y := random next * 400. points add: x@y. gc displayDotOfDiameter: 8 at: points last].

bezier := Bezier start: (points at: 1) end: (points at: 2) controlPoint1: (points at: 3) controlPoint2: (points at: 4). bezier asStroker displayOn: gc.
Create and display a polygon
A polygon is a filled `Polyline` . A polygon can be created and displayed from a collection of vertices.
Send a `displayPolygon:` message to the graphics context of the display surface. The argument is a collection of points, each point representing one vertex of the polygon.
Alternatively, create an instance of `Polyline` by sending a `vertices:` message to the `Polyline` class, with the vertex points as the argument. Wrap the polyline in a stroking or filling wrapper (using `asStroker` or `asFiller` ) and display the wrapped polygon by sending `displayOn:` to the wrapper with the graphics context as argument. A variant of `displayOn:` (used here) enables you to specify the origin--that is, the upper- left corner of the rectangle containing the polygon.
| gc points x y radians polyline origin | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. points := OrderedCollection new. 0 to: 360 by: 30 do: [ :angle | radians := angle degreesToRadians. x := 200 - (200 * radians cos). y := 200 - (200 * radians sin). points add: x@y].

gc displayPolygon: points.

polyline := Polyline vertices: points. 0.9 to: 0.1 by: -0.1 do: [ :scale | gc paint: (ColorValue brightness: 1 - scale). polyline := polyline scaledBy: scale. origin := 200@200 - (polyline bounds width / 2). polyline asFiller displayOn: gc at: origin].
Create and display a rectangle
A `Rectangle` is a special case of a polygon that provides an extended set of operations because it is so commonly used in constructing complex views. A rectangle is commonly created by specifying its origin point and either its lower-right corner or its extent, as shown in the variant.
Create a rectangle (in the example, `rect1` ) by sending an `extent:` message to the point representing the origin. The argument is a point whose x value indicates the width of the rectangle and whose y value indicates the height.
Alternatively, create a rectangle (`rect2` ) by sending a `corner:` message to the origin point. The argument is the lower-right corner point.
Wrap the rectangle in a stroking or filling wrapper (using `asStroker` or `asFiller` ) and display the resulting wrapper on a graphics context.
| gc rect1 rect2 border | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

"Black rectangle" rect1 := 100@100 extent: 200@200. rect1 asFiller displayOn: gc.

"Gray rectangle" border := 3. rect2 := (rect1 origin + border) corner: (rect1 corner - border). rect2 asFiller displayOn: (gc paint: ColorValue gray).
Create and display a circle
A circle is created by specifying its center point and radius, as shown in the basic steps.
Send a `center:radius:` message to the `Circle` class. The first argument is the center point of the circle. The second argument is an integer indicating the radius of the circle.
Wrap the circle in a stroking or filling wrapper by sending `asStroker` or `asFiller` to it.
Display the wrapped circle by sending `displayOn:` to it, with the graphics context as argument.
| gc circle | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

"Blue filled circle" circle := Circle center: 200@200 radius: 100. circle asFiller displayOn: (gc paint: ColorValue blue).

"Black stroked circle" gc paint: ColorValue black; lineWidth: 2. circle asStroker displayOn: gc.
Create and display a filled circle
Send a `displayDotOfDiameter:at:` message to the graphics context of the display surface. The first argument is the diameter of the circle. The second argument is the center point.
| gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

gc displayDotOfDiameter: 200 at: 200@200.
Create and display an ellipse
An ellipse is created by specifying the rectangle that encloses it, as well as the beginning angle and the number of degrees traversed (the sweep angle) from that starting angle. For a complete ellipse, the angles are 0 and 360, as shown in the second variant. When the bounding rectangle is a square, the ellipse is circular.
For a stroked ellipse, send a `displayArcBoundedBy:startAngle:sweepAngle:` message to the graphics context. The first argument is the rectangle that encloses the ellipse. The second argument is 0 and the third argument is 360.
For a filled ellipse, send a `displayWedgeBoundedBy:startAngle:sweepAngle:` message to the graphics context, with the same arguments as above.
Alternatively, create an instance of `EllipticalArc` by sending a `boundingBox:startAngle:sweepAngle:` message to that class. The arguments are the same as above. Then wrap the ellipse in a stroking or filling wrapper and display it on the graphics context.
| gc ellipse | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

"Black stroked ellipse" gc displayArcBoundedBy: (150@100 extent: 100@200) startAngle: 0 sweepAngle: 360.

"Black filled ellipse" gc displayWedgeBoundedBy: (160@110 extent: 80@180) startAngle: 0 sweepAngle: 360.

"Red ellipse" ellipse := EllipticalArc boundingBox: (150@175 extent: 100@50) startAngle: 0 sweepAngle: 360. ellipse asFiller displayOn: (gc paint: ColorValue red)
Create and display an arc
Use the same technique as for displaying a full ellipse, but the `startAngle` argument is the angle at which the arc or wedge begins, measured in degrees clockwise from the 3 o'clock position. The `sweepAngle` argument is the number of degrees spanned by the arc, measured clockwise from the starting angle.
| gc arc box | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. box := 150@100 extent: 100@200.

"Black stroked arc" gc displayArcBoundedBy: box startAngle: 0 sweepAngle: 180.

"Black filled arc" gc displayWedgeBoundedBy: box startAngle: 180 sweepAngle: 90.

"Red arc" arc := EllipticalArc boundingBox: box startAngle: 270 sweepAngle: 90. arc asFiller displayOn: (gc paint: ColorValue red)
Set the line thickness
By default, lines, arcs, and polygons are drawn with a one-pixel line. When you increase the line thickness, extra pixels are spread evenly on both sides of the actual line.
Send a `lineWidth:` message to the graphics context of the display surface. The argument is an integer indicating the number of pixels of thickness.
| gc rect | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. rect := 10@10 extent: 30@30.

2 to: 20 by: 2 do: [ :width | gc lineWidth: width. rect moveBy: 30@30. rect asStroker displayOn: gc].
Set the line cap style
By default, lines and arcs are drawn with butt ends. When two thick lines share an endpoint, butt ends produce a notched joint. Changing the cap style to projecting fixes this by extending each end of the line by half of its thickness. Another solution is to use round ends, which extend the ends in a semicircle.
Send a `capStyle:` message to the graphics context of the display surface. The argument is derived by sending a `capButt` , `capProjecting` , or `capRound` message to the `GraphicsContext` class.
| gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. gc lineWidth: 20.

"Butt line caps -- the default" gc capStyle: GraphicsContext capButt. gc displayLineFrom: 100@100 to: 300@100. gc displayLineFrom: 300@100 to: 300@300.

"Projecting line caps" gc capStyle: GraphicsContext capProjecting. gc displayLineFrom: 100@150 to: 250@150. gc displayLineFrom: 250@150 to: 250@300.

"Round line caps" gc capStyle: GraphicsContext capRound. gc displayLineFrom: 100@200 to: 200@200. gc displayLineFrom: 200@200 to: 200@300.
Set the line join style
By default, a polyline or polygon is drawn with mitered joints. In some situations, a beveled or rounded joint is preferable. The basic step shows how to change the join style.
Send a `joinStyle:` message to the graphics context of the display surface. The argument is derived by sending a `joinMiter` , `joinBevel` , or `joinRound` message to the `GraphicsContext` class.
| gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. gc lineWidth: 30.

"Miter joins -- the default" gc joinStyle: GraphicsContext joinMiter. gc displayPolyline: (Array with: 100@200 with: 200@50 with: 300@200).

"Bevel joins" gc joinStyle: GraphicsContext joinBevel. gc displayPolyline: (Array with: 100@300 with: 200@150 with: 300@300).

"Round joins" gc joinStyle: GraphicsContext joinRound. gc displayPolyline: (Array with: 100@400 with: 200@250 with: 300@400).
Color a graphic
By default, a color-based display surface (`ApplicationWindow` or `Pixmap` ) displays geometric objects in black. You change the color by installing a new paint (color or pattern) in the graphics context.
Send a `paint:` message to the graphics context of the display surface. The argument is a color or pattern.
| gc circle colors | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. circle := Circle center: 200@200 radius: 200. colors := ColorValue constantNames.

colors do: [ :colorName | gc paint: (ColorValue perform: colorName). circle := circle scaledBy: 0.9. circle asFiller displayOn: gc]
Store a color with a graphical object
When the graphic object is going to be reused the color information needs to be kept with it. Using a wrapper keeps track of the paint to be used for its component.
Wrap the geometric object in a stroking or filling wrapper by sending `asStroker` or `asFiller` to it.
Wrap the stroking or filling wrapper in a `GraphicsAttributesWrapper` by sending an `on:` message to that class, with the wrapper from the basic step as the argument.
Create a new `GraphicsAttributes` and send a `paint:` message to it. The argument is a color or pattern.
Install the graphics attributes in the `GraphicsAttributesWrapper` by sending an `attributes:` message with the attributes as the argument.
Display the graphics attributes wrapper by sending a `displayOn:at:` message to it. The first argument is the graphics context of the display surface. The second argument is the origin point at which the geometric object is to be displayed.
| gc circle wrapper1 wrapper2 random pt attributes1 attributes2 | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. circle := Circle center: 0@0 radius: 50.

wrapper1 := GraphicsAttributesWrapper on: circle asFiller. attributes1 := GraphicsAttributes new paint: ColorValue red. wrapper1 attributes: attributes1.

wrapper2 := GraphicsAttributesWrapper on: circle asFiller. attributes2 := GraphicsAttributes new paint: ColorValue blue. wrapper2 attributes: attributes2.

random := Random new. 100 timesRepeat: [ pt := random next * 300 + 50 @ (random next * 300 + 50). wrapper1 displayOn: gc at: pt. pt := random next * 300 + 50 @ (random next * 300 + 50). wrapper2 displayOn: gc at: pt]
Integrate a graphic into an application
Integrating a graphic relies on the fact that a view is automatically sent a `displayOn:` message whenever its containing window is damaged. A model can also redisplay a graphic when it changes
In the view that is responsible for displaying the graphic, create a `displayOn:` method. This method creates the graphic object based on data from the model and displays the graphic objects on the graphics context. This method is triggered whenever an `invalidate` message is received by the view.
displayOn: aGraphicsContext self model isNil ifTrue: [^self].

self model strokes do: [ :stroke | aGraphicsContext displayPolyline: stroke].
In any method in the domain model that affects the graphic, send a `changed:with:` message to `self` . The first argument is a symbol identifying the nature of the change. The second argument is needed by the view to display the appropriate graphic.
add: aPoint "Add aPoint to the current stroke."

self strokes last add: aPoint. self changed: #stroke with: self currentLineSegment.
When the change in the model is such that the view needs no data or control parameter, use `nil` as the second argument in the `changed:with:` message.
eraseAll "Erase my contents."

self strokes removeAll: self strokes copy. self changed: #erase with: nil.
In the view (`SketchView` ), create an `update:with:` method in an updating protocol. This method is invoked by the model whenever it changes and is responsible for updating its display based on the aspect of the model that changed.
update: anAspect with: anObject "When a point is added to the model..." anAspect == #stroke ifTrue: [anObject asStroker displayOn: self graphicsContext].

"When the model erases its contents..." anAspect == #erase ifTrue: [self invalidate].
When an entirely new model is given to the view using its `model:` method, the view sends `invalidate` to itself, again causing a `displayOn:` message to be sent to the view with the appropriate graphics context. Because `model:` overrides an inherited method with that name, begin the method by invoking the inherited version by sending a `model:` message to `super` .
model: aModel super model: aModel. self invalidate.

"Tell the controller where to send menu messages." self controller performer: aModel.
Online example: CustomViewExample , Sketch and SketchView
Images, Cursors, and Icons
Capturing a screen image programmatically
In a Workspace, send a `fromUser` message to the `Image` class. The cursor changes to a cross-hair.
| gc capturedImage | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. capturedImage := Image fromUser. capturedImage displayOn: gc.
Press the <Select> mouse button at the upper-left corner of the desired rectangle, drag to the lower-right corner, and then release the mouse button.
See also:
Capture an image from the screen
Creating a blank image
Send an `extent:depth:palette:` message to the `Image` class. The `extent` argument is a `Point` whose x coordinate controls the width of the image (in pixels) and whose y coordinate controls the height. The `depth` argument is an integer indicating the color depth of the image. The `palette` argument is a color palette from which the image draws its colors.
| blankImage palette | palette := Screen default colorPalette.

blankImage := Image extent: 8@8 depth: (palette depth) palette: palette. ^blankImage
Create an image from a display surface
Send an `asImage` message to a display surface (window, `Pixmap` , or `Mask` ). In the case of a window, the window must not be overlapped by other windows.
| gc window image | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. window := Window currentWindow. window raise.

image := window asImage. image displayOn: gc.
Create an image from a byte array
Send an `extent:depth:palette:bits:pad:` message to the `Image` class. The `bits` argument is a `ByteArray` specifying the color for each pixel, using the color encodings from the palette with a multiple of 32 bits per row. The `pad` argument is 8, 16, or 32. Because each row in the byte array must contain a multiple of 32 bits, the pad size appends 8 bits to a 24-bit row, 16 bits to a 16-bit row (as in the example), or none to a 32-bit row, as a convenience.
| gc lampImage | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

lampImage := Image extent: 16@16 depth: 1 palette: MappedPalette whiteBlack bits: #[ 2r00011111 2r11111000 2r00011111 2r11111000 2r00111111 2r11111100 2r00111111 2r11111100 2r00111111 2r11111100 2r01111111 2r11111110 2r01111111 2r11111110 2r11111111 2r11111111 2r11111111 2r11111111 2r00000011 2r11000000 2r00001111 2r11110000 2r00011111 2r11111000 2r00011111 2r11111000 2r00001111 2r11110000 2r00001111 2r11110000 2r00000111 2r11100000] pad: 16.

lampImage displayOn: gc at: 10@10.
Load an image from a file
Send a `fromFile:` message to class `ImageReader` .
| image | image := (ImageReader fromFile:'myfile.bmp') image. ^bitmap
Specify the bitmap file pathname in place of ' myfile.bmp '.
Display an image
An image can display itself on a graphics context. Note that an image's palette cannot be color-based if you intend to display it on a coverage-based `Mask` rather than a color-based `Window` or `Pixmap` .
Send a `displayOn:` message to the image. The argument is the graphics context of the display surface on which the image is to be displayed.
To specify a display origin other than the default `0@0` , send a `displayOn:at:` message to the image. The first argument is the graphics context and the second argument is a `Point` indicating the origin of the image relative to the display surface's origin.
| gc logo | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo. logo convertForGraphicsDevice: Screen default.

logo displayOn: gc. logo displayOn: gc at: 50@50.
The `convertForGraphicsDevice:` message is necessary to ensure that the image displays properly, by making sure that the color depth and bits per pixel are correct. While it is not always required, it is strongly recommended, especially for images that are read from files.
Create a display surface bearing an image
A common situation requires creating a hidden display surface (`Mask` or `Pixmap` ) of the same size as an image and then displaying the image on it.
Send an `asRetainedMedium` message to the image. If the image has a color-based palette, a `Pixmap` will be returned. If the image has a coverage-based palette, a `Mask` will be returned.
| image pixmap | image := LogoExample logo.

pixmap := image asRetainedMedium. ^pixmap
Display a masked image on a label or button
A graphic label may be displayed in place of text for an action button, radio button, check box, or label.
Select the action button, radio button, check box, or label in the canvas.
In the GUI Painter Tool, turn on the widget's Label is Image property.
In the Message property field, enter the name of the method that returns a graphic image (usually a resource method).
Apply
the properties and Install the canvas.
An `OpaqueImage` may be used as the graphic label with the image background subtracted when it is created from an `Image` and a `Mask` .
Create an accessor method that answers a new `OpaqueImage` created by sending the message `figure:mask:` to it. Provide as arguments the `Image` to serve as the graphic and a matching `Mask` whose pixels for the background to be subtracted are white and the remaining pixels are black.
Enter the name of this method in the Message property field for the widget as in step 3 above.
"Show an opaque image to be used as a label" | gc logo | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := OpaqueImage figure: OpaqueLabelExample logoImage shape: OpaqueLabelExample logoMask. logo displayOn: gc.
Online example: OpaqueLabelExample
Set the color value of a pixel
You can specify the color of a pixel by specifying its color value. The colors must exist in the image's palette.
To get the current color of a pixel, send a `valueAtPoint:` message to the image. The argument is a `Point` indicating the coordinates of the pixel in the image.
To change the color of a pixel, send a `valueAtPoint:put:` message to the image. The first argument is the location of the pixel, and the second is a color that exists in the image's palette.
| gc logo oldColor newColor white black | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo. white := ColorValue white. black := ColorValue black.

"Change each black pixel to white, and vice versa." 0 to: logo height -1 do: [ :y | 0 to: logo width - 1 do: [ :x | oldColor := logo valueAtPoint: x@y. oldColor = white ifTrue: [newColor := black] ifFalse: [newColor := white].

logo valueAtPoint: x@y put: newColor]].

logo displayOn: gc
Set a pixel color by numeric value
To get the current color number of a pixel, send an `atPoint:` message to the image. The argument is a `Point` indicating the coordinates of the pixel in the image. The number that identifies the pixel color in the image's palette is returned.
To change the color of a pixel, send an `atPoint:put:` message to the image. The first argument is the location of the pixel and the second argument is a color number that exists in the image's palette.
| gc logo oldColor newColor | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo.

"Change each black pixel to white, and vice versa." 0 to: logo height -1 do: [ :y | 0 to: logo width - 1 do: [ :x | oldColor := logo atPoint: x@y. oldColor = 1 ifTrue: [newColor := 0] ifFalse: [newColor := 1].

logo atPoint: x@y put: newColor]].

logo displayOn: gc
Mask a rectangular part of an image
Sometimes an image contains extraneous material that needs to be removed.
Create a display surface containing the image by sending an `asRetainedMedium` message to the image.
Send a `completeContentsOfArea:` message to the display surface. The argument is a rectangle that defines the desired portion of the image. The copied portion is returned as an image.
| gc logo subImage pixmap copyRect | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo magnifiedBy: 2@2.

pixmap := logo asRetainedMedium. copyRect := 0@0 extent: (logo width @ logo height / 2) rounded.

subImage := pixmap completeContentsOfArea: copyRect. subImage displayOn: gc at: 10@10.
Mask an irregular part of an image
Create a display surface on which the image has been displayed by sending `asRetainedMedium` to the image.
Create the desired mask by sending an `extent:` message to the `Mask` class. The argument is a `Point` indicating the size of the mask. You can display the desired shape or shapes on the `Mask` as with a window or other display surface (in the example, a solid oval is displayed). The shapes on the mask define the regions through which the image will be visible.
Send a `copyArea:from:sourceOffset:destinationOffset:` message to the graphics context of the destination display surface (in the example, the scratch window). The `copyArea` argument is the mask. The `from` argument is the graphics context of the source display surface (the pixmap containing the logo). The `sourceOffset` argument is a `Point` indicating the origin of the mask when placed over the source display surface. The `destinationOffset` argument is the origin of the subimage when displayed on the destination display surface.
| gc logo pixmap ovalMask | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo magnifiedBy: 2@2. pixmap := logo asRetainedMedium.

ovalMask := Mask extent: 66@66. ovalMask graphicsContext displayWedgeBoundedBy: ovalMask bounds startAngle: 0 sweepAngle: 360.

gc copyArea: ovalMask from: pixmap graphicsContext sourceOffset: 0@0 destinationOffset: 10@10.
Expand/shrink an image
You can get a copy of an image that has been magnified or shrunken in the x dimension, the y dimension, or both, as shown in the basic steps.
To get an expanded copy of an image, send a `magnifiedBy:` message to the image. The argument is a `Point` whose `x` value is multiplied by the width of the image to derive the width of the expanded version; similarly, the `y` value controls the height of the expanded version.
To shrink an image, send a `shrunkenBy:` message to the image. The argument is a point that is used as a divisor to reduce the width and height in the shrunken version.
| gc logo bigLogo tinyLogo | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. logo := LogoExample logo.

bigLogo := logo magnifiedBy: 1@2. tinyLogo := logo shrunkenBy: 1@2.

logo displayOn: gc. bigLogo displayOn: gc at: logo extent. tinyLogo displayOn: gc at: logo extent + bigLogo extent.
Flop an image
Sometimes you need a mirror copy of an image. The basic steps show how to get a reflected copy in which the imaginary mirror is aligned with the x axis, the y axis, or both. This process of rotating an image about the x axis or the y axis is known as flopping an image , from the photographic process in which a negative is flopped onto its backside to produce a mirror image.
To flop an image about the x axis, send a `reflectedInX` message to the image.
To flop an image about the y axis, send a `reflectedInY` message.
To flop an image about both axes, send a `reflectedInX` message followed by a `reflectedInY` message.
| gc helpImage | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. helpImage := ToolbarIconLibrary help20x20 image.

helpImage displayOn: gc at: 10@10. helpImage reflectedInX displayOn: gc at: 60@10. helpImage reflectedInY displayOn: gc at: 10@60. helpImage reflectedInX reflectedInY displayOn: gc at: 60@60.
Rotate an image
You can rotate an image about the z axis in 90-degree increments.
Send a `rotatedByQuadrants:` message to the image. The argument is an integer indicating how many 90-degree rotations you want. A rotated copy of the image is returned.
| gc helpImage rotatedImage | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. helpImage := ToolbarIconLibrary help20x20 image.

rotatedImage := helpImage rotatedByQuadrants: 1.

helpImage displayOn: gc at: 10@10. rotatedImage displayOn: gc at: 60@10.
Reuse a rotated image
Each rotated copy uses time and memory resources. For a series of rotations, you can reduce the resources required by reusing the same scratch image for each subsequent copy. The scratch image must be of the same size as the unrotated image, so this technique works only when all images in the series are the same size.
Create a scratch image the same size as the image that is to be rotated by sending a `copyEmpty` message to the original image.
Send a `rotateByQuadrants:to:` message to the image to be copied. The first argument is the number of quadrants to rotate the image. The second argument is the scratch image.
| gc helpImage scratchImage | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. helpImage := ToolbarIconLibrary help20x20 image.

scratchImage := helpImage copyEmpty.

1 to: 4 do: [ :quads | helpImage rotateByQuadrants: quads to: scratchImage. scratchImage displayOn: gc at: (60 * quads) @ 10]
Layer images
You can achieve a variety of layering effects by combining two images and applying a filtering algorithm to the overlapping portions. VisualWorks provides 16 built-in algorithms, called combination rules , numbered 0 through 15, and the more commonly used rules have names.
To preserve the destination image in its unchanged state, make a copy on which to merge the source image.
Send a `copy:from:in:rule:` message to the destination image (in the example, `triangle` ). The `copy` argument is a rectangle identifying the region in the destination image to be merged with the source image (the lower part of the triangle). The `from` argument is the origin of the rectangle within the source image (the origin of the circle, because we want to copy the entire circle). The `in` argument is the source image. The `rule` argument is an integer identifying a combination rule (which can be derived by sending `and` , `over` , `erase` , `reverse` , `under` , or `reverseUnder` to the `RasterOp` class).
| gc triangle circle scratch | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

triangle := Pixmap extent: 50@100. triangle graphicsContext displayPolygon: (Array with: 0@0 with: 0@50 with: 50@50). triangle := triangle asImage.

circle := Pixmap extent: 50@50. circle graphicsContext displayDotOfDiameter: 50 at: 25@25. circle := circle asImage.

0 to: 15 do: [ :rule | scratch := triangle copy. scratch copy: (0@20 extent: 50@50) from: 0@0 in: circle rule: rule.

scratch displayOn: gc at: (50 * rule \\ 400) @ (50 * rule // 400 * 100)]
Cache an image
A `CachedImage` combines the longevity of an `Image` with the displaying speed of a display surface. Whenever its display surface is unavailable, as when it has been destroyed by a save-and-restart operation, it is recreated from the image automatically. This relieves your application from having to recreate such display surfaces manually.
Note that a `CachedImage` must be treated like a display surface, not an image.
Create a `CachedImage` by sending an `on:` message to that class. The argument is the image that is to be cached.
| gc logo | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

logo := CachedImage on: LogoExample logo. logo displayOn: gc.
Animate an image
Animating an image, or any graphic object, consists of creating a loop in which the object is drawn and erased at successive locations along a path. For smooth animation, use double buffering , which erases and draws only changed pixels.
Create a `Pixmap` of the same size as the window on which the animation is to take place by sending an `extent:` message to the `Pixmap` class. The argument is a rectangle with the window's dimensions, which can be derived by sending a `clippingBounds` message to the window's graphics context.
Create a loop in which the erase-and-display operations occur.
Inside the loop, begin by moving the origin of each object to be animated.
Still inside the loop, erase the `Pixmap` by sending a `clear` message to it.
Still inside the loop, display each animated object in its new location.
Still inside the loop, display the `Pixmap` on the window.
| gc buffer logo windowSize origin1 origin2 jump bufferGC | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. windowSize := gc clippingBounds extent. logo := LogoExample logo. origin1 := 0@0. origin2 := 360@0. jump := 5.

buffer := Pixmap extent: windowSize. bufferGC := buffer graphicsContext.

80 timesRepeat: [ origin1 := origin1 + jump. origin2 := (origin2 x - jump) @ (origin2 y + jump).

"Clear the buffer, then assemble the next scene." buffer clear. logo displayOn: bufferGC at: origin1. logo displayOn: bufferGC at: origin2.

"Display the next scene." buffer displayOn: gc]
Use a standard cursor
The `Cursor` class provides methods for accessing the built-in cursors. Send one of those messages to the `Cursor` class to access the corresponding cursor.
| gc cursor | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

cursor := Cursor wait. cursor displayOn: gc at: 10@10.
Create a new cursor
Create an image that provides the pictorial element in the cursor. If you use the Image Editor to create the image, you must convert its palette to a color-based palette rather than a coverage-based one. To do so, edit the resource method that defines the image, substituting `MappedPalette whiteBlack` (or another two-color palette) for the default `CoveragePalette monoMaskPalette` .
Create a coverage-based image that defines the opaque portion of the first image. The Image Editor can be used to create this image. Typically, it is the same shape as the image from step 1, but completely darkened and one pixel larger on each side.
Create the cursor by sending an `image:mask:hotSpot:name:` message to the `Cursor` class. The `image` argument is the color-based image that you created in step 1. The `mask` argument is the coverage-based image from step 2. The `hotSpot` argument is a point indicating which pixel in the image is the control point. The `name` argument is a string containing a descriptive name for the cursor. The name is of little importance, but it is displayed when you inspect a cursor.
| cursor colorImage maskImage | colorImage := CursorExample townCrierForCursor. maskImage := CursorExample shadow.

cursor := Cursor image: colorImage mask: maskImage hotSpot: 8@8 name: 'townCrier'. cursor showWhile: [(Delay forSeconds: 3) wait].
Online example: CursorExample
Change the current cursor
Changing the cursor is a means of indicating to the user of your application that a certain kind of input is expected or that progress is being made
Send a `showWhile:` message to the cursor. The argument is a block containing the actions that are to take place while the cursor is in its changed state. After the actions in the block are finished, the cursor will return to normal automatically. (In the example, the controller changes the cursor for as long as it holds onto control.)
controlLoop "Change the cursor to a cross-hair for drawing."

Cursor crossHair showWhile: [super controlLoop].
Online example: CustomViewExample and SketchController
Create an icon
Most often used to represent a collapsed window, an icon typically provides a pictorial clue to the nature of the window.
Create an `Image` containing the pictorial element for the icon. You can use the Image Editor to create the image and save it in a method (in the example, the image is returned by the `townCrier` method of the `CursorExample` class).
Create a `Mask` containing the image by sending an `asRetainedMedium` message to the image.
Create an icon by sending an `image:` message to the `Icon` class. The argument is the mask from step 2.
| icon gc image mask |

image := CursorExample townCrier. mask := image asRetainedMedium.

gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

icon := Icon image: mask. icon displayOn: gc at: 10@10.
Note that the `Icon` instance takes care of reconstructing the `Mask` instance when you quit and restart VisualWorks.
Associate an icon with a window
The default icon that VisualWorks displays for each collapsed window may not be appropriate for your application's windows. The basic step shows how to associate a custom icon with a window. This is typically done in the method that creates the window.
Send an `icon:` message to the window. The argument is the icon that is to be displayed when the window is collapsed.
| icon window mask | mask := CursorExample townCrier asRetainedMedium. icon := Icon image: mask. window := ApplicationWindow new.

window icon: icon.

window open. (Delay forSeconds: 1) wait. window collapse.
Colors
Create a color by name
A set of predefined colors is provided by the `ColorValue` class. To create an instance of a predefined color value, send its name to the class:
| gc color | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

color := ColorValue cyan.

gc paint: color. gc displayDotOfDiameter: 400 at: 200@200.
Create an RGB color
Send a `red:green:blue:` message to the `ColorValue` class. All arguments are numbers between zero and one, representing the intensity of their respective colors. (In the example, the intensity of green is varied while the red and blue intensities remain at zero.)
| gc origin | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. origin := 0@0.

1 to: 0 by: -0.01 do: [ :grn | gc paint: (ColorValue red: 0.0 green: grn blue: 0.0). origin := origin + 4. gc displayRectangle: (origin extent: 400 - origin)]
Create an HSB color
Send a `hue:saturation:brightness:` message to the `ColorValue` class. The `hue` argument is a number from 0 to 1, where 0 is red, 0.333 is green, 0.667 is blue, and 1 is red again. The `saturation` argument is a number from 0 to 1, representing minimum vividness (white) to full color; a more saturated color makes an object appear closer to the viewer. The `brightness` argument is a number from 0 to 1, representing minimum brightness (black) to full color; varying the brightness is useful for representing shadows.
| gc r x y | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. r := 50. gc lineWidth: 2.

gc translation: 150@150. 0 to: 1 by: 0.005 do: [ :i | x := (i * Float pi) cos * r. y := (i * Float pi) sin * r / 2. gc paint: (ColorValue hue: 0.0 saturation: 0.5 brightness: i). gc displayLineFrom: x@y to: 0@-100 ].

gc translation: 200@200. 0 to: 1 by: 0.005 do: [ :i | x := (i * Float pi) cos * r. y := (i * Float pi) sin * r / 2. gc paint: (ColorValue hue: 0.0 saturation: 0.75 brightness: i). gc displayLineFrom: x@y to: 0@-100 ].

gc translation: 250@250. 0 to: 1 by: 0.005 do: [ :i | x := (i * Float pi) cos * r. y := (i * Float pi) sin * r / 2. gc paint: (ColorValue hue: 0.0 saturation: 1.0 brightness: i). gc displayLineFrom: x@y to: 0@-100 ]
Create a coverage
In a window or `Pixmap` , each pixel can be assigned a different color. In a `Mask` , each pixel is assigned a level of opaqueness, either 0 (transparent) or 1 (opaque). Each opaque pixel in the mask causes the corresponding pixel in the image to be displayed. A `CoverageValue` is used to represent the level of opaqueness associated with a pixel.
Send a `coverage:` message to the `CoverageValue` class. The argument is 0 (transparent) or 1 (opaque). As an alternative, you can also send a `transparent` or `opaque` message to the `CoverageValue` class.
| gc mask | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. mask := Mask extent: 400@400.

mask graphicsContext displayDotOfDiameter: 400 at: 200@200.

mask graphicsContext paint: (CoverageValue coverage: 0); displayRectangle: (59@59 extent: 283@283).

mask displayOn: gc at: 0@0.
Create a tiled pattern
A `Pattern` is created by filling a space with a single graphic image that is repeated over and over, like tiles covering a floor.
Create the graphic image that will serve as the repeating tile in the pattern. You can also use a window, `Pixmap` , or `Mask` as the tile.
Send an `asPattern` message to the tile.
| gc tile | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

tile := Image parcPlaceDigitalkLogo shrunkenBy: 4@4. tile := tile asPattern.

gc paint: tile. gc displayRectangle: (50@50 extent: 300@300).
Adjust a pattern's phase
By default, the first tile in the pattern is displayed at the origin of the display surface. You can shift that first tile, and with it the entire pattern. This shift, known as the tile phase, is sometimes helpful for aligning the edges of the tiles with the edges of the graphic object that is being painted, as in the variant.
Send a `tilePhase:` message to the graphics context of the display surface on which the patterned object is to be displayed. The argument is a point that defines the origin of the first tile in the pattern. (As in the example, the tile phase is often the same as the origin of the painted object, which aligns the tiles with the top and left edges of the object.)
| gc tile | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. tile := Image parcPlaceDigitalkLogo shrunkenBy: 4@4. tile := tile asPattern. gc paint: tile.

gc tilePhase: 50@50.

gc displayRectangle: (50@50 extent: 300@300).
Apply a color or pattern
Send a `paint:` message to the graphics context of the display surface on which the object is to be displayed. The argument is a color, a pattern, or in the case of a `Mask` , a coverage.
| gc tile | tile := Pixmap extent: 10@10. gc := tile graphicsContext.

"Tile background" gc paint: ColorValue chartreuse. gc displayRectangle: (0@0 extent: 10@10).

"Tile foreground" gc paint: ColorValue red. gc displayDotOfDiameter: 10 at: 4@4.

"Patterned circle" gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. gc paint: tile asPattern. gc displayDotOfDiameter: 400 at: 200@200.
Change a color palette
In a graphic image, each pixel is associated with a color in the image's palette of colors. One way of changing the color scheme is to substitute a different palette.
Create an array containing the new colors. To access the existing palette's array of colors, send a `palette` message to the image, and then send a `colors` message to the resulting palette. You can then modify the color array as desired (in the example, we locate the white entry and substitute the color yellow).
Create a new palette by sending a `withColors:` message to the `MappedPalette` class. The argument is the new or modified color array.
Install the new palette by sending a `palette:` message to the image. The argument is the new palette.
| gc palette image colors whiteIndex | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. image := InputFieldSpec paletteIcon asImage.

colors := image palette colors. whiteIndex := colors indexOf: ColorValue white. colors at: whiteIndex put: ColorValue yellow.

palette := MappedPalette withColors: colors.

image := image palette: palette. image displayOn: gc at: 10@10.
Convert an image to use the default palette
Send a `convertToPalette:` message to the image. The argument is the default color palette, which can be accessed by sending a `default` message to the `Screen` class and then sending a `colorPalette` message to the resulting screen object. (For a coverage-based image, send a `coveragePalette` message instead of `colorPalette` .)
| gc image | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. image := Image parcPlaceDigitalkLogo magnifiedBy: 2@2.

image := image convertToPalette: Screen default colorPalette. image displayOn: gc at: 10@10.
Change color rendering policy
When a graphic image contains a color that does not exist in the screen's palette, a neighboring color is used instead. VisualWorks provides three different renderers for deciding which color to substitute for a missing color. Of the three, `NearestPaint` is the fastest and is the default on color screens. `OrderedDither` is the default on monochrome and gray-scale screens. `ErrorDiffusion` uses a more sophisticated color-blending algorithm than `OrderedDither` does, but it tends to be slower.
Send a `convertForGraphicsDevice:renderedBy:` message to the image. The first argument is typically `Screen default` . The second argument is `NearestPaint` when you want to use the nearest available paint from the screen's palette, `OrderedDither` when you want to synthesize the nonexistent color by blending neighboring colors, and `ErrorDiffusion` when you want to use a more sophisticated (but often slower) blending algorithm.
"THIS EXAMPLE IS INTENDED TO BE USED ON A COLOR SCREEN. IT CAN TAKE SEVERAL MINUTES." | gc r g b im | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. im := Image extent: 60@60 depth: 15 palette: (FixedPalette redShift: 10 redMask: 31 greenShift: 5 greenMask: 31 blueShift: 0 blueMask: 31). 0 to: 59 do: [:x | 0 to: 59 do: [:y | r := 1 - ((x@y - (10@10)) r / 30) max: 0. g := 1 - ((x@y - (20@50)) r / 30) max: 0. b := 1 - ((x@y - (50@30)) r / 30) max: 0. im atPoint: x@y put: (im palette indexOfPaintNearest: (ColorValue red: r green: g blue: b))]].

(im convertForGraphicsDevice: Screen default renderedBy: NearestPaint new) displayOn: gc at: 10@10.

(im convertForGraphicsDevice: Screen default renderedBy: OrderedDither new) displayOn: gc at: 80@10.

(im convertForGraphicsDevice: Screen default renderedBy: ErrorDiffusion new) displayOn: gc at: 150@10.
Convert an image to a Palette
Send a `convertToPalette:renderedBy:` message to the image. The first argument is the desired palette (in the example, a monochrome palette). The second argument is the desired renderer (a `NearestPaint` , an `OrderedDither` , or an `ErrorDiffusion` ).
| gc r g b im | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext. im := Image extent: 60@60 depth: 15 palette: (FixedPalette redShift: 10 redMask: 31 greenShift: 5 greenMask: 31 blueShift: 0 blueMask: 31). 0 to: 59 do: [:x | 0 to: 59 do: [:y | r := 1 - ((x@y - (10@10)) r / 30) max: 0. g := 1 - ((x@y - (20@50)) r / 30) max: 0. b := 1 - ((x@y - (50@30)) r / 30) max: 0. im atPoint: x@y put: (im palette indexOfPaintNearest: (ColorValue brightness: 1-((1-r)*(1-g)*(1-b))))]].

(im convertToPalette: MappedPalette whiteBlack renderedBy: NearestPaint new) displayOn: gc at: 10@10.

(im convertToPalette: MappedPalette whiteBlack renderedBy: OrderedDither new) displayOn: gc at: 80@10.

(im convertToPalette: MappedPalette whiteBlack renderedBy: ErrorDiffusion new) displayOn: gc at: 150@10.
Set the rendering policy for nonimage graphics
Install a paint policy in the graphics context of the display surface by sending a `paintPolicy:` message to the graphics context. The argument is a `PaintPolicy` , typically a new instance.
Set the rendering algorithm by sending a `paintRenderer:` message to the paint policy. The argument is a `NearestPaint` or an `OrderedDither` (but not an `ErrorDiffusion` , which is only used with images). By default, a new `OrderedDither` has an order of 6, which means it synthesizes 65 (2 to the sixth, plus 1) intermediate color values between each pair of neighboring colors in the palette. You can set the order by sending an `order:` message to the `OrderedDither` class to create an instance; the argument is the desired order number.
| gc | gc := (ExamplesBrowser prepareScratchWindow) graphicsContext.

gc paintPolicy: (PaintPolicy new imageRenderer: OrderedDither new). gc paintPolicy paintRenderer: NearestPaint new. 0 to: 60 by: 4 do: [:i | 0 to: 60 by: 4 do: [:j | gc paint: (ColorValue red: i/60 green: j/60 blue: 0). gc displayRectangle: (i@j+(10@10) extent: 4@4)]].

gc paintPolicy paintRenderer: (OrderedDither order: 1). 0 to: 60 by: 4 do: [:i | 0 to: 60 by: 4 do: [:j | gc paint: (ColorValue red: i/60 green: j/60 blue: 0). gc displayRectangle: (i@j+(80@10) extent: 4@4)]].

gc paintPolicy paintRenderer: (OrderedDither order: 6). 0 to: 60 by: 4 do: [:i | 0 to: 60 by: 4 do: [:j | gc paint: (ColorValue red: i/60 green: j/60 blue: 0). gc displayRectangle: (i@j+(150@10) extent: 4@4)]].