Вы находитесь на странице: 1из 550

IBM Smalltalk

Programmers Reference
Version 5.5
Note

Before using this document, read the general information under Notices on page ix.

May 2001
This edition applies to Version 5.5 of the VisualAge Smalltalk products, and to all subsequent releases and
modifications until otherwise indicated in new editions. Make sure you are using the correct edition for the level of
the product. The term VisualAge, as used in this publication, refers to the VisualAge Smalltalk product set.
Portions of this book describe materials developed by Object Technology International Inc. of Ottawa, Ontario,
Canada. Object Technology International Inc. is a subsidiary of the IBM Corporation.
If you have comments about the product or this document, address them to: IBM Corporation, Attn: IBM Smalltalk
Group, 621-107 Hutton Street, Raleigh, NC 27606-6324. You can fax comments to (919) 828-9633.
When you send information to IBM, you grant IBM a nonexclusive right to use or distribute the information in any
way it believes appropriate without incurring any obligation to you.
Copyright International Business Machines Corporation 1994, 2000. All rights reserved.
US Government Users Restricted Rights Use, duplication or disclosure restricted by GSA ADP Schedule Contract
with IBM Corp.
Contents
Notices . . . . . . . . . . . . . . . ix Magnitude classes . . . . . . . . . . . . 13
Trademarks . . . . . . . . . . . . . . ix Magnitude comparing . . . . . . . . . . 14
Association . . . . . . . . . . . . . 14
About this book . . . . . . . . . . . xi Character . . . . . . . . . . . . . . 14
Date . . . . . . . . . . . . . . . . 16
What this book includes . . . . . . . . . . xi
Time. . . . . . . . . . . . . . . . 16
Who this book is for . . . . . . . . . . . xi
Number . . . . . . . . . . . . . . 17
About this product or feature . . . . . . . . xi
Integer . . . . . . . . . . . . . . . 18
Conventions used in this book . . . . . . . . xii
Fraction . . . . . . . . . . . . . . 19
References. . . . . . . . . . . . . . . xii
Float. . . . . . . . . . . . . . . . 19
Common Language Data Types, Common
ScaledDecimal . . . . . . . . . . . . 20
Language Implementation, and Common Process
Graphical classes . . . . . . . . . . . . 20
Model . . . . . . . . . . . . . . . xii
Point . . . . . . . . . . . . . . . 20
Common File System . . . . . . . . . . xiii
Rectangle . . . . . . . . . . . . . . 21
Common Graphics. . . . . . . . . . . xiii
Stream classes . . . . . . . . . . . . . 22
Common Widgets . . . . . . . . . . . xiii
Accessing . . . . . . . . . . . . . . 23
Common Printing . . . . . . . . . . . xiv
Copying . . . . . . . . . . . . . . 23
Dynamic Data Exchange . . . . . . . . . xiv
Enumerating . . . . . . . . . . . . . 23
National Language Support. . . . . . . . xiv
Positioning . . . . . . . . . . . . . 24
Tell us what you think . . . . . . . . . . xiv
Reading . . . . . . . . . . . . . . 24
Testing . . . . . . . . . . . . . . . 24
Chapter 1. IBM Smalltalk overview . . . 1 Truncating . . . . . . . . . . . . . . 24
Base subsystems . . . . . . . . . . . . . 1 Writing . . . . . . . . . . . . . . . 24
Common Language Data Types . . . . . . . 1 Creating instances . . . . . . . . . . . 24
Common Language Implementation . . . . . 1 Support classes . . . . . . . . . . . . . 24
Common Process Model . . . . . . . . . 1 UndefinedObject. . . . . . . . . . . . 24
Common File System . . . . . . . . . . 1 Message and DirectedMessage . . . . . . . 25
Common Graphics . . . . . . . . . . . 2 Block . . . . . . . . . . . . . . . 25
Common Widgets . . . . . . . . . . . 2 Exception handling classes . . . . . . . . . 25
Extended Widgets . . . . . . . . . . . 2 ExceptionalEvent . . . . . . . . . . . 25
Drag and Drop . . . . . . . . . . . . 2 Signal . . . . . . . . . . . . . . . 25
Common Printing . . . . . . . . . . . 2 Creating new exceptions . . . . . . . . . 25
Design intent . . . . . . . . . . . . . . 2 Signaling an exception. . . . . . . . . . 26
Architecture . . . . . . . . . . . . . . 3 Handling of exceptions . . . . . . . . . 26
Industry-standard support . . . . . . . . . . 3 ExceptionalEvent collections . . . . . . . . 28
Platform support . . . . . . . . . . . . . 4 Completion blocks . . . . . . . . . . . 29
Default exception handler . . . . . . . . 30
Chapter 2. Common Language Data System exceptions . . . . . . . . . . . 30
Types . . . . . . . . . . . . . . . . 5 Examples of exception handling . . . . . . 30
Object behavior . . . . . . . . . . . . . 6
Basic dependents mechanism . . . . . . . . 6 Chapter 3. Common Language
Behavior testing . . . . . . . . . . . . 6 Implementation . . . . . . . . . . . 33
Class identity testing . . . . . . . . . . 6 Behavior messages . . . . . . . . . . . . 34
Copying . . . . . . . . . . . . . . . 6 Class queries . . . . . . . . . . . . . 34
Printing and storing . . . . . . . . . . . 6 Compiling . . . . . . . . . . . . . . 34
Performing . . . . . . . . . . . . . . 7 Creating instances . . . . . . . . . . . 34
Error handling . . . . . . . . . . . . . 7 Enumeration . . . . . . . . . . . . . 35
Primitive accessing . . . . . . . . . . . 7 Instance accessing . . . . . . . . . . . 35
Mutating . . . . . . . . . . . . . . 7 Instance structure testing . . . . . . . . . 35
Testing object equality or identity . . . . . . 7 Method accessing . . . . . . . . . . . 35
Boolean classes . . . . . . . . . . . . . 7 Method adding and deleting . . . . . . . 36
Boolean messages. . . . . . . . . . . . 7 Method queries . . . . . . . . . . . . 36
Collection classes . . . . . . . . . . . . . 8 Class messages . . . . . . . . . . . . . 36
Collection protocols . . . . . . . . . . . 9 Class variable accessing . . . . . . . . . 37
Common collection protocols . . . . . . . 10 Instance variable accessing . . . . . . . . 37
Unique collection protocol . . . . . . . . 12

Copyright IBM Corp. 1994, 2000 iii


Shared pool accessing . . . . . . . . . . 37 Reading directory entries . . . . . . . . . 59
Class accessing . . . . . . . . . . . . 37 Closing the directory descriptor . . . . . . 61
Initializing and removing . . . . . . . . . 38 Using file streams . . . . . . . . . . . . 61
Superclass accessing . . . . . . . . . . 38 File stream classes . . . . . . . . . . . 61
Metaclass messages. . . . . . . . . . . . 38 Opening and closing file streams . . . . . . 61
Accessing . . . . . . . . . . . . . . 38 Reading and writing file streams . . . . . . 62
Creating new classes . . . . . . . . . . . 38 Characters versus bytes . . . . . . . . . 63
Creating fixed classes . . . . . . . . . . 39 Line delimiters . . . . . . . . . . . . 63
Creating variable classes . . . . . . . . . 39 Using low-level file operations . . . . . . . . 64
Creating variable byte classes . . . . . . . 39 Opening files . . . . . . . . . . . . . 64
Extended classes. . . . . . . . . . . . . 39 Closing file descriptors . . . . . . . . . 65
Multiple instance accessing . . . . . . . . 40 Reading and writing data . . . . . . . . 66
String converting . . . . . . . . . . . 40 Changing the file offset . . . . . . . . . 66
Compiling and evaluating code. . . . . . . . 40 Other low-level operations . . . . . . . . 67
Compiling . . . . . . . . . . . . . . 41 Mixing streams and file descriptors . . . . . . 67
Evaluating. . . . . . . . . . . . . . 41 Using access modes and flags with file streams 67
CompiledMethod . . . . . . . . . . . . 42 Performing low-level file operations on streams 68
Accessing . . . . . . . . . . . . . . 42 File locking and share modes . . . . . . . . 68
Testing . . . . . . . . . . . . . . . 42 File locking . . . . . . . . . . . . . 68
EmSystemConfiguration . . . . . . . . . . 42 Share modes . . . . . . . . . . . . . 70
Handling errors . . . . . . . . . . . . . 71
Chapter 4. Common Process Model . . 45 Suppressing system error dialogs . . . . . . 73
Creating a process . . . . . . . . . . . . 45 Testing existence and obtaining other file properties 74
Suspending, resuming, and ending a process . . . 46 Obtaining a CfsStat instance . . . . . . . . 74
Setting and modifying process priorities . . . . . 46 Mixing platform-specific and Common File System
Synchronization using semaphore and delay . . . 47 operations . . . . . . . . . . . . . . . 76
Block evaluation methods . . . . . . . . 48 Performing a platform-specific call with a
Process-related block methods . . . . . . . 49 CfsFileDescriptor . . . . . . . . . . . 76
Process methods . . . . . . . . . . . . 49 Converting a platform file descriptor into a
ProcessorScheduler methods. . . . . . . . 50 CfsFileDescriptor . . . . . . . . . . . 76
Delay class and instance methods . . . . . . 50 Obtaining platform-specific error information . . 76
Semaphore class and instance methods . . . . 51
Chapter 6. Common Graphics . . . . . 79
Chapter 5. Common File System. . . . 53 X Window system graphics library compatibility . . 79
Accessing the capabilities of the Common File Core Common Graphics class hierarchy . . . . . 80
System . . . . . . . . . . . . . . . . 53 Seldom-used and abstract classes . . . . . . . 82
Basic file protocols . . . . . . . . . . . 53 A simplified drawing process overview . . . . . 83
Stream protocols. . . . . . . . . . . . 53 Before drawing . . . . . . . . . . . . 83
Portability protocols . . . . . . . . . . 53 During drawing . . . . . . . . . . . . 83
Error handling protocols . . . . . . . . . 53 After drawing . . . . . . . . . . . . 83
CfsConstants pool dictionary . . . . . . . 54 A simple example of the drawing process . . . 83
Basic classes . . . . . . . . . . . . . 54 CgConstants pool dictionary. . . . . . . . 83
Specifying file names and paths . . . . . . . 54 Using graphics contexts . . . . . . . . . . 84
Portable file names . . . . . . . . . . . 54 Basic graphics context methods . . . . . . . 84
File system roots . . . . . . . . . . . 55 Creating graphics contexts . . . . . . . . 85
Path separators . . . . . . . . . . . . 55 Copying graphics contexts . . . . . . . . 88
Managing files and directories . . . . . . . . 55 Changing graphics contexts . . . . . . . . 89
Current working directory . . . . . . . . 55 Freeing graphics contexts . . . . . . . . . 89
Creating and removing directories . . . . . . 55 Using graphics contexts with other drawables . . 89
Deleting files . . . . . . . . . . . . . 56 Drawing operations . . . . . . . . . . . 89
Renaming files . . . . . . . . . . . . 56 Drawing points . . . . . . . . . . . . 90
Copying files . . . . . . . . . . . . . 56 Drawing lines . . . . . . . . . . . . 90
Startup directory . . . . . . . . . . . 57 Drawing rectangles . . . . . . . . . . . 91
Obtaining volume information . . . . . . . . 57 Drawing polygons . . . . . . . . . . . 92
Volume name and type . . . . . . . . . 57 Drawing arcs and circles . . . . . . . . . 92
File names and directory path case . . . . . 57 Drawing pie slices and chords using filled arcs 93
File name length. . . . . . . . . . . . 57 Using fonts . . . . . . . . . . . . . . 93
Volumes with different file name lengths . . . 58 A simplified view of the font process . . . . . 94
Searching directories . . . . . . . . . . . 58 Querying the system for fonts . . . . . . . 95
Opening a directory for searching . . . . . . 58 Loading fonts. . . . . . . . . . . . . 97
Assigning fonts for use in drawing operations 100

iv IBM Smalltalk: Programmers Reference


String drawing operations with fonts . . . . 101 The parent-child widget tree . . . . . . . 138
Releasing CgFonts and CgFontStructs from The widget lifecycle . . . . . . . . . . 138
memory . . . . . . . . . . . . . . 102 Mapping and unmapping widgets . . . . . 140
Obtaining the current font from a graphics Managing and unmanaging widgets. . . . . 141
context . . . . . . . . . . . . . . 102 Widget resources and functions . . . . . . 141
Using cursors . . . . . . . . . . . . . 103 CwConstants pool dictionary . . . . . . . 144
The process for using cursors . . . . . . . 103 Example code to create a widget tree . . . . 144
Font cursors . . . . . . . . . . . . . 103 Widget event handling and callbacks . . . . 146
Glyph cursors . . . . . . . . . . . . 104 Example of using an event handler and a
Pixmap cursors . . . . . . . . . . . . 105 callback . . . . . . . . . . . . . . 146
Changing the color of a cursor . . . . . . 105 Creating and using widgets . . . . . . . . 148
Platform cursors . . . . . . . . . . . 106 Widget creation convenience methods . . . . 148
Using pixmaps . . . . . . . . . . . . . 106 Callbacks . . . . . . . . . . . . . . 150
Creating a pixmap using createPixmap:. . . . 107 Event handlers . . . . . . . . . . . . 154
Creating a pixmap using Shell widgets . . . . . . . . . . . . . 158
createPixmapFromBitmapData: . . . . . . 107 Top-level shell widgets . . . . . . . . . . 158
Copying pixmaps to and from windows . . . 108 Scrolled-window widgets . . . . . . . . . 160
Getting pixmap geometry . . . . . . . . 108 Main-window widgets . . . . . . . . . . 161
Creating a bitmap from bitmap data. . . . . 109 Main windows and geometry management . . 161
Creating stipples using bitmaps . . . . . . 109 Text widgets. . . . . . . . . . . . . . 162
Writing bitmaps to files . . . . . . . . . 110 Drawing area widgets . . . . . . . . . . 164
Reading bitmaps from files . . . . . . . . 111 Adding an event handler to a drawing area . . 166
Displaying bitmaps . . . . . . . . . . 111 Layout widgets. . . . . . . . . . . . . 167
Common Graphics image support . . . . . . 112 Form widgets . . . . . . . . . . . . 167
Specifying colors . . . . . . . . . . . . 113 Row-column widgets . . . . . . . . . . 169
Specifying colors as RGB intensities . . . . . 113 Button and label widgets . . . . . . . . . 171
Specifying colors by name . . . . . . . . 113 Static label widgets . . . . . . . . . . 172
Parsing a color-specification string . . . . . 114 Push-button widgets . . . . . . . . . . 172
Using palettes . . . . . . . . . . . . . 114 Toggle-button widgets . . . . . . . . . 173
The default palette . . . . . . . . . . 115 Radio-button groups . . . . . . . . . . 174
Creating indexed palettes . . . . . . . . 116 Check boxes . . . . . . . . . . . . . 175
From pixel values to colors and back . . . . 116 Icon and pixmap label and button widgets . . 176
Selecting and drawing with palettes . . . . . 117 Application-drawn buttons . . . . . . . . 177
Direct palettes . . . . . . . . . . . . 120 Menus. . . . . . . . . . . . . . . . 178
Device-independent images . . . . . . . . 120 Greying out buttons . . . . . . . . . . 178
Creating and manipulating images . . . . . 121 Simple menus and menu bars . . . . . . . 179
Displaying images. . . . . . . . . . . 122 Creating a menu bar and pull-down menu
Direct color images . . . . . . . . . . 122 using simple menu protocol . . . . . . . 180
Copying images from a drawable . . . . . 123 Creating a secondary menu using simple menu
Icons . . . . . . . . . . . . . . . . 124 protocol . . . . . . . . . . . . . . 181
Creating icons . . . . . . . . . . . . 124 Creating a pop-up menu using simple menu
Drawing icons . . . . . . . . . . . . 125 protocol . . . . . . . . . . . . . . 183
Loading icons from DLLs . . . . . . . . 125 Non-simple menus and menu bars . . . . . 184
Using operating system icons . . . . . . . 126 Non-simple menu example . . . . . . . . 184
Image and icon file formats . . . . . . . . 126 List widgets . . . . . . . . . . . . . . 185
Loading images from files . . . . . . . . 127 Single selection lists . . . . . . . . . . 186
Handling errors . . . . . . . . . . . 127 Multiple selection lists . . . . . . . . . 187
Loading icons from files . . . . . . . . . 128 Scrolled lists . . . . . . . . . . . . . 188
Unloading images into files . . . . . . . 129 Combo-box widgets . . . . . . . . . . . 189
Unloading icons into files . . . . . . . . 129 Composite-box widgets . . . . . . . . . . 190
Unloading images and icons into ByteArrays 130 MessageBox widgets . . . . . . . . . . 191
Loading images and icons from ByteArrays . . 130 SelectionBox widgets . . . . . . . . . . 193
Determining the format of a file . . . . . . 131 Dialog convenience methods . . . . . . . 196
Extra file format information . . . . . . . 131 Creating and using prompters . . . . . . . . 197
Resource management summary . . . . . . . 133 Message prompter. . . . . . . . . . . 198
Text prompter . . . . . . . . . . . . 200
Chapter 7. Common Widgets. . . . . 135 File selection prompter . . . . . . . . . 200
OSF/Motif compatibility . . . . . . . . . 135 Extended widgets . . . . . . . . . . . . 201
Common Widgets class hierarchy. . . . . . . 135 Writing an extended widget . . . . . . . 202
Overview of Common Widgets user interface Example: a primitive extended widget . . . . 203
concepts . . . . . . . . . . . . . . . 137 Example: a composite extended widget. . . . 207

Contents v
Fonts . . . . . . . . . . . . . . . . 211 Leaving a target . . . . . . . . . . . 268
Using the system browser font . . . . . . 212 Dropping. . . . . . . . . . . . . . 268
Colors . . . . . . . . . . . . . . . . 213 Canceling a drag . . . . . . . . . . . 269
Clipboard operations . . . . . . . . . . . 213 System configuration . . . . . . . . . . . 269
Examples for using the clipboard . . . . . . 214 Simple drag and drop . . . . . . . . . . 270
Platform-integrated drag and drop . . . . . . 215 Widget limitations . . . . . . . . . . . . 270
Target types . . . . . . . . . . . . . 216
Transferring data . . . . . . . . . . . 216 Chapter 10. Common Printing . . . . 273
Drag and drop objects . . . . . . . . . 217 Common Printing classes . . . . . . . . . 273
Procs . . . . . . . . . . . . . . . 217 Printing process overview . . . . . . . . . 274
Common Widgets drag and drop classes . . . 217 Selecting a printer . . . . . . . . . . . 274
Drag source widgets and the CwDragContext Configuring print job attributes . . . . . . 275
object . . . . . . . . . . . . . . . 217 Creating a print job . . . . . . . . . . 275
Drop site widgets and CwDropSite objects . . 219 Using the printer prompter . . . . . . . . . 275
Data transfer and the CwDropTransfer object 223 Print job attributes. . . . . . . . . . . 276
The user interface process model . . . . . . . 225 Using a printer shell . . . . . . . . . . . 276
The system view . . . . . . . . . . . 226 Creating a shell. . . . . . . . . . . . 277
The application programmers view . . . . . 228 Adding callbacks . . . . . . . . . . . 277
Examples of applications with long-running Starting a job . . . . . . . . . . . . 278
operations . . . . . . . . . . . . . 231 Producing a page . . . . . . . . . . . 278
Ending a job. . . . . . . . . . . . . 279
Chapter 8. Extended Widgets . . . . 233 CwPrinterShell resources and convenience
Extended Widgets class hierarchy . . . . . . 233 methods . . . . . . . . . . . . . . 279
EwConstants pool dictionary . . . . . . . . 233 Printing with Common Graphics . . . . . . . 279
Creation convenience methods . . . . . . . 234 A complete example . . . . . . . . . . . 280
Extended list widgets. . . . . . . . . . . 234 Setting up printers and queues on UNIX platforms 282
Common resources . . . . . . . . . . . 235 Configuring printer setup options . . . . . 283
Scrolled lists . . . . . . . . . . . . . . 235 Printer configuration information. . . . . . 284
Drawn list widget . . . . . . . . . . . . 236
Icon list widgets . . . . . . . . . . . . 237 Chapter 11. IBM Smalltalk Virtual
Renderables . . . . . . . . . . . . . 238 Machine API . . . . . . . . . . . . 285
Direct editing of labels . . . . . . . . . 239
Who should read this chapter . . . . . . . . 285
Flowed icon list widget . . . . . . . . . . 240
Conventions . . . . . . . . . . . . . 285
Icon area widget . . . . . . . . . . . . 241
IBM Smalltalk C programming model . . . . . 286
Table list widget . . . . . . . . . . . . 241
Defined types . . . . . . . . . . . . 286
Table list resources . . . . . . . . . . 242
Object types . . . . . . . . . . . . . 286
Table columns . . . . . . . . . . . . 243
Macros versus functions . . . . . . . . . 288
Direct editing of cell values. . . . . . . . 245
External language interface . . . . . . . . . 288
Edit policies . . . . . . . . . . . . . 246
Parameter types . . . . . . . . . . . 288
Tree widgets. . . . . . . . . . . . . . 247
Calling a PlatformFunction . . . . . . . . 290
Icon trees. . . . . . . . . . . . . . 248
Inline external function calls . . . . . . . 290
Table trees . . . . . . . . . . . . . 249
PlatformFunction protocols . . . . . . . . 291
Notebook widgets . . . . . . . . . . . . 250
PlatformLibrary protocols . . . . . . . . 293
Creating pages . . . . . . . . . . . . 250
Entry points . . . . . . . . . . . . . . 294
Callbacks . . . . . . . . . . . . . . 250
Parameter types and return types . . . . . 295
PM notebook widget . . . . . . . . . . 251
Calling an EsEntryPoint . . . . . . . . . 296
WIN notebook widget . . . . . . . . . 253
EsEntryPoint protocols . . . . . . . . . 297
Progress bar widget . . . . . . . . . . . 253
Asynchronous callouts . . . . . . . . . . 298
Slider widget . . . . . . . . . . . . . 254
Calling a Platform Function asynchronously . . 298
Spin button widget . . . . . . . . . . . 256
Locking resources for an asynchronous call . . 301
Split window widget . . . . . . . . . . . 257
ACO errors and error cases. . . . . . . . 301
Tool bar widget . . . . . . . . . . . . 259
Walkbacks . . . . . . . . . . . . . 302
Creating tools . . . . . . . . . . . . 259
ACO errors . . . . . . . . . . . . . 302
Using tools . . . . . . . . . . . . . 260
Parameter types and return types . . . . . 303
Managing resources . . . . . . . . . . 303
Chapter 9. Drag and Drop . . . . . . 263 Extensions to platform function protocols . . . 305
Drag and drop adapters . . . . . . . . . . 263 ACO resource manager protocols. . . . . . 306
Sequence of events . . . . . . . . . . . 264 Resource future protocols . . . . . . . . 307
Voting and cursors . . . . . . . . . . 267 Static future protocols . . . . . . . . . 308
Source vote and image changes . . . . . . 268 ACO error protocols . . . . . . . . . . 308

vi IBM Smalltalk: Programmers Reference


OSObjects . . . . . . . . . . . . . . 308 Test cases. . . . . . . . . . . . . . . 372
OSObject subclasses . . . . . . . . . . 308 Spreadsheet . . . . . . . . . . . . . 373
OSObject protocols . . . . . . . . . . 310 Spreadsheet window coordinates . . . . . . 373
OSImmediate protocols . . . . . . . . . 310 Two windows exchanging data . . . . . . 373
OSBaseType, OSObjectPointer, and OSStructure Updating time and date . . . . . . . . . 374
protocols . . . . . . . . . . . . . . 311 Updating time in the String format . . . . . 374
OSStructure protocols . . . . . . . . . 315 Platform-specific support . . . . . . . . . 374
OSVariableStructure protocols . . . . . . . 315
OSBaseType protocols . . . . . . . . . 315 Chapter 13. National Language
ObjectPointer protocols . . . . . . . . . 316 Support . . . . . . . . . . . . . . 377
Methods available in other classes . . . . . 317
Overview of IBM Smalltalk National Language
User primitives . . . . . . . . . . . . . 318
Support . . . . . . . . . . . . . . . 377
User primitive tables . . . . . . . . . . 319
NLS concepts and definitions . . . . . . . 377
Functions available in user primitives . . . . 320
The POSIX locale model . . . . . . . . . 379
Asynchronous messages (interrupts). . . . . . 327
Overview of internationalization . . . . . . 379
Using user primitive functions outside user
Overview of localized messages . . . . . . 380
primitives . . . . . . . . . . . . . . 328
Overview of message catalogs . . . . . . . 382
Sample user primitives for IBM Smalltalk . . . . 328
Locating message catalogs . . . . . . . . 382
Sample callback for OS/2 and Microsoft Windows 330
National Language Support classes . . . . . 383
Example callback code . . . . . . . . . 330
Support for double-byte characters . . . . . 383
Smalltalk code that uses the primitive above 332
Obtaining Locale, LCCType, and LCCollate
Platform requirements . . . . . . . . . . 333
objects. . . . . . . . . . . . . . . 384
OS/2 . . . . . . . . . . . . . . . 333
Obtaining LCMessages, LCMonetary,
Microsoft Windows 95, Windows 98, and
LCNumeric, and LCTime objects . . . . . . 385
Windows NT . . . . . . . . . . . . 334
NLS-enabled classes . . . . . . . . . . 386
Pascal16 and cdecl16 PlatformFunctions . . . 334
Locale-specific sorting . . . . . . . . . 387
Pascal16 and cdecl16 pointer conversion . . . 335
Number formatting . . . . . . . . . . 387
UNIX platforms . . . . . . . . . . . 335
Locale change notification and the image
Solaris platforms . . . . . . . . . . . 336
startup sequence . . . . . . . . . . . 388
Primitive error codes . . . . . . . . . . . 336
Tools for developing international software . . . 389
Loading externalization tools . . . . . . . 389
Chapter 12. Dynamic data exchange 339 Using message templates . . . . . . . . 389
Introduction . . . . . . . . . . . . . . 339 Referencing and initializing localized messages 391
DDE concepts and definitions . . . . . . . . 339 Overview of the message localization process 392
DDE classes . . . . . . . . . . . . . . 342 Using message catalogs . . . . . . . . . 393
DdeClient class . . . . . . . . . . . . 342 Using external message dictionaries . . . . . 394
DdeServer class . . . . . . . . . . . 343 Using indexed external messages . . . . . . 397
DdeServerManager class. . . . . . . . . 343 Deleting locales from a message catalog file . . 398
DdeCallbackData class . . . . . . . . . 343 Support for extended locales . . . . . . . 399
Building a DDE server . . . . . . . . . . 344 Compatibilities . . . . . . . . . . . . 400
Building a DDE server that uses default Compression . . . . . . . . . . . . 402
processing . . . . . . . . . . . . . 344 Limitations . . . . . . . . . . . . . 403
Building a DDE server that does not use default Error detection and description . . . . . . 403
processing . . . . . . . . . . . . . 347 Support for platform resource formats . . . . 405
Discussion of the DdeServerManager . . . . 352 Localization support API . . . . . . . . 408
Building a DDE client . . . . . . . . . . 357 IBM Smalltalk locale names. . . . . . . . . 411
An example DDE client . . . . . . . . . 358 Manual localization . . . . . . . . . . . 412
Discussion of the DdeClient . . . . . . . 359 LCCollate . . . . . . . . . . . . . 412
Formats of data transferred between DDE servers LCMessages . . . . . . . . . . . . . 413
and clients . . . . . . . . . . . . . . 362 LCMonetary . . . . . . . . . . . . . 413
Callbacks for handling DDE events . . . . . . 362 LCNumeric . . . . . . . . . . . . . 414
Return values . . . . . . . . . . . . 363 LCTime . . . . . . . . . . . . . . 414
DDE protocols . . . . . . . . . . . . . 364 Locale . . . . . . . . . . . . . . . 414
DdeServerManager public class methods . . . 364 Locales and platform representation . . . . . . 415
DdeServerManager public instance methods . . 364 OS/2 Presentation Manager . . . . . . . 415
DdeClass public instance methods . . . . . 367 Microsoft Windows . . . . . . . . . . 418
DdeServer public instance methods . . . . . 368 AIX, HP-UX, and Solaris . . . . . . . . 420
DdeClient public instance methods . . . . . 369
Protocols common to DdeServer and DdeClient 371 Chapter 14. Inter-process
Converting Smalltalk objects to a ByteArray and
back . . . . . . . . . . . . . . . . 372
communication . . . . . . . . . . 425

Contents vii
UNIX environment . . . . . . . . . . . 425 Getting and setting properties . . . . . . . 459
Method specification . . . . . . . . . . 425 Releasing an OLE automation object. . . . . 460
Usage example . . . . . . . . . . . . 426 Using OLE custom controls (OCXs) . . . . . . 460
UNIX process . . . . . . . . . . . . . 427 Creating the OLE main window . . . . . . 461
Method specification . . . . . . . . . . 428 Creating OCXs . . . . . . . . . . . . 461
Usage example . . . . . . . . . . . . 430 Implementing an ambient-property handler . . 462
UNIX pipe stream . . . . . . . . . . . . 431 Using automation aspects of an OCX . . . . 463
UNIX read pipe stream method specification 431 Implementing event handlers . . . . . . . 464
UNIX write pipe stream method specification 434 Wrapping OCXs with extended widgets . . . 466
Using licensed OCXs . . . . . . . . . . 467
Chapter 15. Object Linking and
Embedding (OLE) . . . . . . . . . 437 Appendix A. Widget resources and
OLE concepts and definitions . . . . . . . . 438 callbacks . . . . . . . . . . . . . 469
OLE classes . . . . . . . . . . . . . . 440
OleMainWindow . . . . . . . . . . . 440 Appendix B. Extended widgets
OlePrimitive. . . . . . . . . . . . . 441 resources and callbacks . . . . . . 491
OleClient . . . . . . . . . . . . . . 441
OleControl . . . . . . . . . . . . . 442
OleAutomationObject . . . . . . . . . 442 Appendix C. Drag and drop resources
OleFile . . . . . . . . . . . . . . 442 and callbacks . . . . . . . . . . . 513
Building an OLE document container . . . . . 442
Creating an OLE main window . . . . . . 443 Appendix D. Common graphics
Creating OLE clients . . . . . . . . . . 444 platform differences . . . . . . . . 515
Enabling clipboard operations . . . . . . . 449
Creating an object verb menu . . . . . . . 451
Invoking the Links dialog . . . . . . . . 454
Appendix E. Common widgets
Saving and retrieving an OLE client . . . . . 455 platform differences . . . . . . . . 517
Using OLE automation . . . . . . . . . . 457
Creating OLE Automation Objects . . . . . 458 Index . . . . . . . . . . . . . . . 525
Invoking methods of OLE automation objects 458

viii IBM Smalltalk: Programmers Reference


Notices
References in this publication to IBM products, programs, or services do not imply
that IBM intends to make these available in all countries in which IBM operates.
Any reference to an IBM product, program, or service is not intended to state or
imply that only that IBM product, program, or service may be used. Any
functionally equivalent product, program, or service that does not infringe any of
the intellectual property rights of IBM may be used instead of the IBM product,
program, or service. The evaluation and verification of operation in conjunction
with other products, except those expressly designated by IBM, are the
responsibility of the user.

IBM may have patents or pending patent applications covering subject matter in
this document. The furnishing of this document does not give you any license to
these patents. You can send license inquiries, in writing, to the IBM Director of
Licensing, IBM Corporation, 500 Columbus Avenue, Thornwood, NY, USA 10594.

IBM may change this publication, the product described herein, or both.

Trademarks
The following terms are trademarks of the IBM Corporation in the United States or
other countries or both:

AIX IBM International Business Machines


OS/2 VisualAge Presentation Manager

The following terms are trademarks of other companies:

HP-UX Hewlett Packard


Microsoft Microsoft Corporation
OCX Microsoft Corporation
OLE Microsoft Corporation
Sun Solaris Sun Microsystems
Windows Microsoft Corporation
Windows NT Microsoft Corporation

Windows is a trademark of Microsoft Corporation.

UNIX is a registered trademark in the United States and other countries licensed
exclusively through X/Open Company Limited.

Copyright IBM Corp. 1994, 2000 ix


x IBM Smalltalk: Programmers Reference
About this book
This book provides reference information for the IBM Smalltalk development
environment.

What this book includes


This book is designed to explain the concepts and functionality of the IBM
Smalltalk subsystems. Some subtle or seldom-used aspects of each subsystem are
not described in this book.

Chapter 1. IBM Smalltalk overview on page 1 provides you with a general


understanding of how IBM Smalltalk is structured, and should be read first. Each
of the remaining chapters describes a specific IBM Smalltalk subsystem and can be
read in any order.

Because IBM Smalltalk is based on several industry standard programming


interfaces, familiarity with these standards is helpful, although not essential, to
understanding the IBM Smalltalk subsystems. An understanding of the X Window
System Xlib graphics model is helpful when programming Common Graphics
operations. Similarly, familiarity with the X Toolkit Intrinsics (Xt) and the
OSF/Motif Widget set (Xm) is helpful when programming Common Widgets. The
Common File System is based on POSIX.1 (UNIX) file system interfaces. The
Common Language Data Types, Common Language Implementation, and
Common Process Model are primarily based on the interfaces described in the
Smalltalk-80 Blue Book and IBM Red Book.

Who this book is for


This book is intended to be used by programmers who want to do the following:
v Become familiar with the base functionality provided by IBM Smalltalk.
v Interface to other languages or write custom primitive operations.
v Exchange data between applications in the style of a client/server architecture.
v Develop international software.

It is assumed that the reader has previous programming experience and a working
knowledge of the following:
v Object-oriented programming concepts
v The Smalltalk programming language
v The IBM Smalltalk programming environment

About this product or feature


VisualAge enables you to quickly make client/server applications.

VisualAge makes you more productive by providing interactive visual


programming tools and a set of parts that represent the client/server spectrum.
You create applications by assembling and connecting parts. In many cases, you do
not even have to write code.

If you do need to write code, VisualAge provides a state-of-the-art, integrated


development environment. The programming language you use is Smalltalk, an
industry-standard, highly productive, pure object-oriented language.

Copyright IBM Corp. 1994, 2000 xi


Conventions used in this book
This book uses several conventions that you might not have seen in other product
manuals.

Tips and environment-specific information are flagged with icons:

Shortcut techniques and other tips

VisualAge for OS/2

VisualAge for Windows

VisualAge for UNIX platforms

These highlighting conventions are used in the text:

Highlight
style Used for Example
Boldface New terms the first time they are VisualAge uses construction from
used parts to develop software by
assembling and connecting reusable
components called parts.
Items you can select, such as push Select Add Part from the Options
buttons and menu choices pull-down. Type the parts class and
select OK.
Italics Special emphasis Do not save the image.
Titles of publications Refer to the VisualAge Smalltalk Users
Guide.
Text that the product displays The status area displays Category:
Data Entry.
VisualAge programming objects, such Connect the windows
as attributes, actions, events, composite aboutToOpenWidget event to the
parts, and script names initializeWhereClause script.
Monospace VisualAge scripts and other examples doSomething
font of Smalltalk code | aNumber aString |
aNumber := 5 * 10.
aString := 'abc'.
Text you can enter For the customer name, type John
Doe

References
The following are suggested references for IBM Smalltalk programmers.

Common Language Data Types, Common Language


Implementation, and Common Process Model
A. Goldberg and D. Robson, Smalltalk-80: The Language and Its Implementation (the
Blue Book), Addison-Wesley, 1983

xii IBM Smalltalk: Programmers Reference


Giffin Lorimer, Dieter Neumann, Rick DeNatale, and Yen-Ping Shan, Smalltalk
Portability: A Common Base (the Red Book), IBM International Technical Support
Centers, 1992, GG24-3903 (proposed as a draft ANSI Smalltalk standard)

Common File System


Fred Zlotnick, The POSIX.1 Standard, A Programmers Guide, Benjamin/Cummings,
Redwood City, Calif., 1991

Common Graphics
James L. Conger, Windows API Bible: The Definitive Programmers Reference, New
York, N.Y., Waite Group Press, 1992

Brian Myers and Eric Hamer, Mastering Windows NT Programming, New York, N.Y.,
SyBex, 1993

Ney ed., Xlib Programming Manual, Volume One, OReilly & Associates, Inc.,
Sebastopol, Calif., 1988

Nye ed., Xlib Reference Manual, Volume Two, OReilly & Associates, Inc., Sebastopol,
Calif., 1988

Scheifler and Gettys, X Windows System, 2nd ed., Digital Press

Steve Rimmer, Bit-Mapped Graphics, 2nd ed., New York, N.Y.: Wincrest/McGraw-
Hill, 1993

Steve Rimmer, Supercharged Bitmapped Graphics, 1st ed., New York, N.Y.,
Wincrest/McGraw-Hill, 1992

An Aldus/Microsoft Techinical Memorandum, revision 5.0 Redmond, Wash., Aldus


Corporation and Microsoft Corporation, 1988

Frieze Graphics Technical Reference Manual (including information for Publishers


Paintbrush, PC Paintbrush Plus, and PC Paintbrush), ZSoft Corporation

Presentation Manager Programming Reference Volume III, OS/2 2.0 Technical Library,
IBM, 1992

X11R4 and X11R5 reference manuals

Any other manuals discussing the X Window Systems graphics capabilities (Xlib).

Common Widgets
Asente and Swick, X Window System Toolkit, Digital Press

Eric F. Johnson and Kevin Reichard, Power Programming... Motif (Second Edition),
New York, N.Y., MIS Press, 1993

Nye and OReilly, X Toolkit Intrinsics Programming Manual OSF/Motif Eidtion,


Volume Four, Sebastopol, Calif., OReilly & Associates, Inc., 1990

Nye and OReilly, X Toolkit Intrinsics Reference Manual, Volume Five, Sebastopol,
Calif., OReilly & Associates, Inc., 1990

About this book xiii


Open Software Foundation, OSF/Motif Programmers Reference Revision 1.1,
Englewood Cliffs, N.J., Prentice Hall, 19901991

Open Software Foundation, OSF/Motif Programmers Guide Revision 1.1,


Englewood Cliffs, N.J., Prentice Hall, 19901991

Common Printing
Axel Deininger, An X Print Server-Bringing WYSIWYG to X, The X Resource, X
(April 1994), 141

Uniforum, CDE Snapshot #1 CD-ROM, October 1993

Uniforum, CDE Snapshot #1 CD-ROM, April 1994

Dynamic Data Exchange


IBM OS/2 Programming Guide

Microsoft Win32 Application Programming Reference

National Language Support


Donald Lewine, POSIX Programmers Guide: Writing Portable UNIX Programs,
Sebastopol, Calif., OReilly & Associates, Inc., 1991

National Language Design Guide, Designing Enabled Products, Volume 1, 2nd ed., IBM
reference SE09800101

National Language Support Reference Manual, Volume 2, 2nd Ed., IBM reference
SE09800101

X/Open Portability Guide, XSI Supplementary Definitions, X/Open Company Ltd.,


Englewood Cliffs, N.J., Prentice Hall, 1989

Tell us what you think


The VisualAge Smalltalk web site has a comment form. Please take a few moments
to tell us what you think about this book. The only way for us to know if you are
satisfied with our books or if we can improve their quality is through feedback
from customers like you.

xiv IBM Smalltalk: Programmers Reference


Chapter 1. IBM Smalltalk overview
VisualAge is a development environment designed to support the easy creation of
software applications on multiple platforms. IBM Smalltalk provides a
platform-independent application program interface (API) based on industry
standards. VisualAge was built using IBM Smalltalk.

Comprising nine subsystems, IBM Smalltalk encompasses the functionality


required by most Smalltalk applications. Applications programmed entirely in
accordance with the IBM Smalltalk interface specification run without modification
on all supported platforms, yet adopt the native look and feel of the particular
platform. This enables applications to be produced from a single code base across
multiple platforms, while still providing a high degree of platform integration.

IBM Smalltalk directly utilizes the native functionality provided by each platform.
IBM Smalltalk emulates API functionality not directly supported by a particular
platform in order to support a complete and consistent interface across all
supported platforms. As part of the implementation of IBM Smalltalk, access to
platform-specific functionality is also provided.

Base subsystems
IBM Smalltalk comprises nine subsystems:

Common Language Data Types


The Common Language Data Types (CLDT) subsystem contains the classes and
associated methods for working with standard Smalltalk objects such as Collection,
Magnitude, Boolean, and Stream.

Common Language Implementation


The Common Language Implementation (CLIM) subsystem contains the classes
and associated methods which, together with the virtual machine, implement the
Smalltalk class and message mechanisms. In most cases, the implementation of
CLIM is inseparably tied to the virtual machine and includes Behavior, Class,
Compiler, and CompiledMethod. (The virtual machine is the program that provides
the Smalltalk execution environment on a single machine. It maps data and logic to
the executing machine architecture, isolating the Smalltalk code from the
architecture of the machine.)

Common Process Model


The Common Process Model (CPM) subsystem contains the classes and associated
methods that facilitate the execution and arbitration of multiple execution contexts,
or Smalltalk processes.

Common File System


The Common File System (CFS) subsystem contains the classes and associated
methods for performing file and directory operations.

Copyright IBM Corp. 1994, 2000 1


Common Graphics
The Common Graphics (CG) subsystem contains the classes and associated
methods for performing low-level graphics operations, such as drawing lines,
circles, and rectangles and for manipulating device independent images, fonts, and
colors.

Common Widgets
The Common Widgets (CW) subsystem contains the classes and associated
methods for constructing application user interfaces through the composition of
user interface components called widgets.

Extended Widgets
The Extended Widgets (EW) subsystem contains the classes and associated
methods for expanding on graphical user interfaces built using the Common
Widgets subsystem.

Drag and Drop


Through the Drag and Drop (DD) subsystem, pluggable drag and drop support is
provided for the widgets in both Common Widgets and Extended Widgets
subsystems without requiring modifications to the widgets. The Drag and Drop
subsystem uses the portable CG and CW subsystems.

Common Printing
The Common Printing (CP) subsystem contains the classes and associated methods
for performing printer job and page control, setup, and graphical and text output.

Design intent
The increasing number of platforms supporting graphical user interfaces has
resulted in a demand for simultaneous deployment of applications on multiple
platforms. At the same time, applications are expected to adopt platform look and
feel standards in order to be accepted by end users. One-shot porting is not a
viable option, because maintenance becomes very difficult if applications cannot be
single sourced. Applications need to meet the following requirements:
v It must be possible to single source applications across diverse platforms.
v Applications must conform to the look and feel standards of each platform.
v For maximum performance and platform integration, applications must leverage
native functionality wherever possible.
v Programmers capable of developing portable applications are in increasing
demand. It is expensive for organizations to move their development teams to
new, non-standardized software technologies. There is a growing emphasis on
software standards as a means to maximize the return on investment when
training developers.

IBM Smalltalk addresses these requirements by providing a fully documented API


that supports the same functionality on all platforms. This environment offers a
common interface through which developers can make use of API functionality
regardless of the operating system or hardware in use. Using IBM Smalltalk,
software developers can create applications that are fully portable between
supported platforms and that provide full support for platform look and feel.

2 IBM Smalltalk: Programmers Reference


Architecture
The following diagram provides a conceptual view of the IBM Smalltalk
architecture. The lowest level represents the specific target platform. The target
platform consists of a particular hardware platform and operating system. The
operating system might have built-in capabilities for graphical user interfaces, or it
might be augmented by support packages such as OSF/Motif and the X Window
System.

IBM Smalltalk provides the virtual machine layer implemented for the target
platform. IBM Smalltalk is built on the native capabilities of the platform, together
with additional platform-specific classes and methods developed in order to
support the full IBM Smalltalk API.

Developers working in the IBM Smalltalk environment have the capabilities of all
layers at their disposal to use as the needs of the product and organization dictate.
Of course, developers sacrifice portability when they make use of the layers below
the common classes and methods. However, even in such cases, they can use the
development environment to identify and control non-portable parts of the
application.

Application classes and


methods Development environment

Common classes and methods


Platform-specific classes and methods

Platform interface classes and methods

IBM Smalltalk virtual machine


OS/2 Microsoft AIX ...
PM Windows OSF/Motif platforms

Industry-standard support
Unlike most Smalltalk class libraries that were developed in the absence of
standards, IBM Smalltalk was developed based on existing industry software and
hardware standards. The use of these standards is a major benefit to development
teams because it means that Smalltalk developers can use the same terminology
and application protocols used in other languages. This reduces the need for
vendor-specific Smalltalk documentation and training for the base class libraries,
window systems, file systems, and graphics and process models. It also leverages a
corporations investment in standards training and documentation.

IBM Smalltalk is based on the following industry standards:


v The X Window System
v OSF/Motif
v POSIX.1
v Smalltalk-80: The Language and Its Implementation (commonly referred to as the
Blue Book because of its distinctive cover)
v Smalltalk Portability: A Common Base(proposed as a draft ANSI Smalltalk standard
and often referred to as the Red Book)

Chapter 1. IBM Smalltalk overview 3


The following table lists the subsystems and the standards upon which they are
based.
Table 1. Industry Standards
IBM Smalltalk Subsystem Based on These Industry Standards
Common Language Data Types Smalltalk-80 Blue Book and IBM Red Book
Common Language Implementation Smalltalk-80 Blue Book and IBM Red Book
Common Process Model Smalltalk-80 Blue Book
Common File System POSIX.1 and Smalltalk-80 Blue Book
Common Graphics X Window System
Common Widgets OSF/Motif
Common Printing X Window System

Although not essential, familiarity with the above standard interfaces is helpful to
understanding and using the IBM Smalltalk subsystems.

Platform support
IBM Smalltalk supports a wide variety of hardware and operating systems. In each
case, the implementation emphasizes those capabilities of the platform that are
standardized, efficiently implemented, or essential to achieving native look and
feel. For example, the native widgets provided by OSF/Motif are used as the
underlying implementation of the standard widgets on those platforms supporting
OSF/Motif. Similarly, Windows controls are used on the Microsoft Windows
platform.

Wherever possible, the required functionality is mapped directly to the equivalent


platform capability. Where the specified functionality is not provided by the
platform, it is emulated to support portability.

4 IBM Smalltalk: Programmers Reference


Chapter 2. Common Language Data Types
The Common Language Data Types subsystem (CLDT) provides a
platform-independent interface and implementation for the basic data types that
form the building blocks of the Smalltalk programming environment. For
convenience the CLDT classes have been grouped into seven categories: Boolean,
collection, magnitude, graphical, stream, support, and exception handling.

The specification for CLDT is based primarily on two sources:


v Smalltalk-80: The Language and Its Implementation (the Blue Book)
v Smalltalk Portability: A Common Base (the Red Book)

All data types in the CLDT subsystem are concrete classes, that is, they are meant
to be instantiated and used directly in programs. To avoid constraining
implementations, no inheritance hierarchy has been defined for CLDT. However,
there is a set of basic messages that all the CLDT data types are assumed to
support. Because in all existing implementations this common behavior is
implemented in class Object, it will be referred to as Object behavior. It is
convenient to include a description of Object behavior in this section, even though
Object itself is not a CLDT data type because it is an abstract class.

Our aim in this section is to provide an overview of CLDT, assuming that readers
are generally familiar with Smalltalk.

Note: One of the primary motivations in the design of CLDT was to achieve
maximum functionality with minimum complexity. Most inexperienced
Smalltalk developers complain that the class library is too large, needlessly
complicated, and consequently difficult to learn. There is clearly a trade-off
between the utility and additional functionality provided by a large library,
and the steep learning curve that it entails. The methods in CLDT have been
chosen because they provide real added value, by which we mean that they
truly save developers both time and effort.

In addition, we will occasionally provide some tips to developers who already


know the base Smalltalk/V or Objectworks\Smalltalk dialects, and want to leverage
that knowledge to get up to speed quickly on CLDT. Because these tips are
especially useful when you are porting some existing code to IBM Smalltalk, these
paragraphs are marked by the legend Porting tip.

Porting tip: These tips compare and contrast CLDT with the base classes in the
unmodified versions of Smalltalk/V or Objectworks\Smalltalk as
shipped by the vendors. Differences between CLDT messages and
those supported in Smalltalk/V or Objectworks\Smalltalk are noted.
Many of these differences result from different strategies for error
handling and other implementation-dependent features. While CLDT
sometimes omits a method that is included in either Smalltalk/V or
Objectworks\Smalltalk, in most cases the omitted method is either
rarely used or has a trivial implementation. The preferred Smalltalk
programming style is to in-line trivial methods; accordingly we have
not cluttered the CLDT class libraries with methods that are seldom or
never used.

Copyright IBM Corp. 1994, 2000 5


Object behavior
As noted above, all CLDT classes (indeed, all classes in IBM Smalltalk), are
required to support the basic Object behavior described in the sections that follow.

Basic dependents mechanism


addDependent:, broadcast:, broadcast:with:, changed, changed:, dependents, release,
removeDependent:, update:

Note: For details on the dependents mechanism see Chapter 14 of the Blue Book.
The dependents mechanism is really a legacy from earlier Smalltalk graphics
implementations. It was used to force updates in cases where several
windows provided views on the same object or several closely related
objects. It is no longer used in modern GUI systems like Common Graphics
and Common Widgets. Consequently, we have included only the basic
protocol in CLDT.

Porting tip: All Smalltalk dialects use the same basic messages to implement the
dependents mechanism, with one exception: Smalltalk/V does not
support removeDependent:. Both Smalltalk/V and Objectworks\Smalltalk
provide a number of additional (and different) messages that extend
the basic mechanism.

Behavior testing
class, isKindOf:, isMemberOf:, isNil, notNil, respondsTo:

Class identity testing


isCharacter, isClass, isFloat, isInteger, isMetaclass, isDBString, isSBString, isString,
isSymbol

Porting tip: The class identity testing messages are all short (and often optimized)
forms of:
"self class = {NameOfClass}"
"self isKindOf: {NameOfClass}"

Each Smalltalk dialect has its own list of short forms; the choices are
usually driven by implementation details. Smalltalk/V has a large
number of such methods, while Objectworks\Smalltalk uses only a few.
In CLDT we have chosen a middle course. Note that Smalltalk/V uses
the spelling MetaClass rather than the Blue Book spelling Metaclass,
which is used in CLDT and Objectworks\Smalltalk.

Copying
copy

Note: Unlike other Smalltalk implementations, CLDT does not provide a public
default version of either deepCopy or shallowCopy in Object protocol. The
semantics of both these messages are normally specific to each class, and
often to an application.

Printing and storing


printOn:, printString, storeOn:, storeString

6 IBM Smalltalk: Programmers Reference


Performing
perform:, perform:with:, perform:with:with:, perform:with:with:with:,
perform:withArguments:

Porting tip: You should avoid using these methods because they adversely affect
the packaging process. Refer to the guidelines in the packaging
materials of IBM Smalltalk Users Guide for more information.

Error handling
doesNotUnderstand:, error:, primitiveFailed, halt, halt:, shouldNotImplement,
subclassResponsibility

Porting tip: These methods provide only the most basic error handling.
Smalltalk/V uses implementedBySubclass n place of the Blue Book
method subclassResponsibility, and does not support shouldNotImplement
or halt:. All Smalltalk dialects provide additional error handling
methods that are system- and operating system-specific.

Primitive accessing
basicSize, instVarAt:, instVarAt:put:

Mutating
become:

Porting tip: The exact semantics of become: (and Array>>multiBecome:) are not
consistent across different Smalltalk platforms, particularly in the use
of the two-way become (in which the receiver and argument actually
switch identities) and the one-way become (in which the receiver is
de-referenced and garbage collected). IBM Smalltalk uses a one-way
become. See the Blue Book for more complete information on become:.

Testing object equality or identity


=, =, ==, , hash, yourself

Boolean classes
The Boolean classes are True and False. Booleans are used in Smalltalk to
implement both standard and application-defined control constructs. Although it
might seem strange that Smalltalk does not provide any built-in mechanisms for
controlling program running, this turns out not to be a limitation. Indeed, it adds a
surprising degree of flexibility to the language.

Boolean messages
&, and:, eqv::, ifFalse:, ifFalse:ifTrue:, ifTrue:, ifTrue:ifFalse:, not, or:, xor:, |

Porting tip: All Smalltalk dialects implement the same set of Boolean messages.
Users should be aware that in some cases the compiler can optimize
by in-lining control operations rather than implementing them as
message sends. This is usually transparent unless there is an attempt
to subclass or modify one of the Boolean classes.

Chapter 2. Common Language Data Types 7


Collection classes
The CLDT collection classes are Array, Bag, ByteArray, DBString, Dictionary,
IdentityDictionary, Interval, LookupTable, OrderedCollection, Set, SortedCollection, String
and Symbol. They are described in Chapters 9 and 10 of the Blue Book.

Note: Three seldom-used Blue Book collection classes have been excluded from
CLDT, namely LinkedList, MappedCollection, and RunArray. The design of
LinkedList betrays too much of its implementation and is not an
industrial-strength List abstract data type. RunArray was used mainly in the
original Smalltalk graphics system to store bitmaps. MappedCollection allows
for one level of indirection in Dictionary accesses and is seldom used in
practice. Moreover, in most cases, the Dictionary message at:ifPresent: can be
used to achieve the same effect.
A collection is a group of objects that can be manipulated and operated on either
individually or as a whole. The objects contained by a collection are called its
elements. The number of elements in a collection is its size. Most collections are
generic containers that can hold any kind of object, but five collections (ByteArray,
DBString, Interval, String, and Symbol) place restrictions on the class of their
elements. As a rule, developers should use care when defining recursive
collections, that is, collections that have themselves, directly or indirectly, as one of
their own elements. In a recursive collection there are operations, for example, =,
that can fail in some implementations.

The elements of a collection can be operated on as a whole using enumeration


methods that traverse each element of the collection. The canonical enumeration
method is do:. These enumeration methods replace the for, while, or do constructs
used in traditional programming languages to build iterative procedures. Any
collection that is ordered has a standard enumeration order, that is, a standard
order in which elements are traversed by the do: operation. They are in fact
traversed in order of monotonically increasing indices. This ordering is guaranteed
to be maintained from traversal to traversal. Unordered collections by definition
have no standard traversal order and are therefore traversed in an undefined order.
In particular, subsequent traversals of the same unordered collection are not
guaranteed to access elements in the same order.

Collections can be indexed or nonindexed. Indexed collections allow individual


elements to be accessed by means of an external key. The key can be either an
integer or an arbitrary object, depending on the collection.

Bag and Set are the simplest kinds of collections, because they are unordered and
do not support an indexing mechanism. Bags can contain duplicate elements, while
Sets cannot. Otherwise, their behavior is identical. Neither Bags nor Sets can
contain an instance of UndefinedObject (usually referred to as nil); that is, attempts
to add nil to a Bag or Set are ignored. The elements of a Bag or Set cannot be
individually accessed, because only group operations are supported.

A Dictionary is an unordered collection whose elements are accessed by an


explicitly assigned external key. Keys are often strings or symbols, but in principle
any object can be used as a Dictionary key. Key matching for storing or accessing
elements is based on the equality operation =. Consequently, keys must be unique
with respect to the equality operation, that is, two elements cannot be associated
with the same key nor can two keys be equal. There are no restrictions on the
elements stored in a Dictionary.

8 IBM Smalltalk: Programmers Reference


IdentityDictionary is a specialization of Dictionary, that uses the equivalence
operation (==) rather than equality (=) to match keys. In practice, the keys of an
IdentityDictionary are usually symbols, although it is not a requirement.

LookupTable has the same protocols as Dictionary but does not contain any
associations. This makes LookupTable faster than Dictionary for everything except
associationsDo:. It also requires less space.

The elements of the ordered collections are all indexed by integer keys that begin
at 1 and increase. The ordered collections are Array, ByteArray, DBString,
OrderedCollection, SortedCollection, String, and Symbol. OrderedCollection offers
the most comprehensive protocol of any of the collection classes, and is
consequently the one used most often by developers. OrderedCollections do not
have a fixed size, but can grow as needed to hold additional elements. The
elements of an OrderedCollection can be any class of object. SortedCollection is similar
to OrderedCollection, except that the elements are held in sorted order. The sort
ordering is determined by the sort block associated with the SortedCollection. A sort
block is a block with two formal parameters, which answers a Boolean value when
provided with any two elements of the collection. If the answer is true then meter
should be sorted before the second, whereas false indicates that the opposite order
is correct.

The size of Array, ByteArray, DBString, String, and Symbol collections is fixed
when they are created, and cannot be changed. Fixed-size collections are normally
implemented as contiguous blocks of memory; consequently they can usually
support efficient accessing, copying, and traversing operations. While the elements
of an Array can be any object, the elements of a ByteArray can be integers between
0 and 255 (in other words, bytes). The elements of DBString, String and Symbol
must be characters. The elements of String must be characters ranging in value
from 0 to 255, whereas the elements of DBString can be characters in the range 0 to
65535, that is, double-byte characters. A Symbol is distinguished from a String in
that it is immutable, that is, a Symbol is a read-only object once it has been created.

Note: Symbols cannot contain characters whose value exceeds 255.

Porting tip: The class DBString is analagous to the class TwoByteString in


Objectworks\Smalltalk, and to the class DoubleByteString in
Smalltalk/V. Because CLDT does not support Symbols that contain
double-byte characters, the Objectworks\Smalltalk class TwoByteSymbol,
and Smalltalk/V class DoubleByteSymbol have no CLDT equivalent.

An Interval is not really a collection at all, but a generator that represents a


geometric progression. The elements of an Interval must be numbers (integers,
fractions, or floats). Rather than store its elements, an Interval computes them when
required, using the start, stop, and increment values provided when it is created.

Collection protocols
The collection classes support a very rich protocolso rich that at times it can be
confusing. To help structure the following section, the method names are grouped
in loosely defined functional categories. The protocol categories are described in
the following section, and the table shows a cross-reference mapping between
classes and the protocols they support. A difficulty arises in cases where two or
more classes support the same message name, but the semantics of the message are
not identical. Usually, one message is just a refinement of the other, that is, it
imposes additional restrictions on the implementation of the message. A more

Chapter 2. Common Language Data Types 9


serious problem arises when the messages have completely different meanings but
share the same name. This document will identify such cases explicitly.

Common collection protocols


These same protocols are supported by many different types of collections.

The following table describes protocols supported by CLDT collection classes.

Accessing
after:, at:, basicAt:, before:, findFirst:, findLast:, first, indexOf:, indexOf:ifAbsent:,
indexOfSubCollection:startingAt:, indexOfSubCollection:startingAt:ifAbsent:, last

Porting tip: Smalltalk/V does not support indexOfSubCollection:startingAt: or


indexOfSubCollection:startingAt:ifAbsent:.

Adding
add:, addAll:

Byte accessing
byteAt:, byteAt:put:

Porting tip: Smalltalk/V does not support byteAt: and byteAt:put:.

Converting
asArray, asBag, asByteArray, asOrderedCollection, asSet, asSortedCollection,
asSortedCollection:

Copying
,, copyFrom:to:, copyReplaceAll:with:, copyReplaceFrom:to:with:,
copyReplaceFrom:to:withObject:, copyReplacing:withObject:, copyWith:, copyWithout:,
reverse

Note: copyReplaceFrom:to:withObject: and copyReplacing:withObject: are not in the


Blue Book, but we have found them to be useful.

Porting tip: Smalltalk/V does not support the following:


v copyReplaceAll:with:
v copyReplaceFrom:to:withObject:
v copyReplacing:withObject:

Objectworks/Smalltalk does not support the following:


v copyReplaceFrom:to:withObject:
v copyReplacing:withObject:

Smalltalk/V uses reversed instead of reverse. Smalltalk/V uses different


semantics for copyWithout:. In Smalltalk/V, only the first element equal
to the argument is omitted from the copy, while in IBM Smalltalk (all
platforms) and Objectworks\Smalltalk, all elements equal to the
argument are left out.

Creating instances (class methods)


new, new:, with:, with:with:, with:with:with:, with:with:with:with:

Creating dictionaries (class methods)


new, new:

10 IBM Smalltalk: Programmers Reference


Dictionary accessing
add:, addAll:, associationAt:, associationAt:ifAbsent:, at:, at:ifAbsent:, at:ifAbsentPut:,
at:ifPresent:, at:put:, includesKey:, keyAtValue:, keyAtValue:ifAbsent:, keys,
removeAllKeys:, removeAllKeys:ifAbsent:, removeKey:, removeKey:ifAbsent:, values

Note: Several messages in this protocol have the same names as messages in other
protocols but have different semantics: add:, addAll:, at:, and at:put:. The use
of these names is well established in the Smalltalk community and has not
been changed in CLDT. The messages at:ifAbsentPut:, at:ifPresent:,
removeAllKeys:, and removeAllKeys:ifAbsent: are not in the Blue Book, but have
been included in CLDT because their use results in more compact and
readable code.

Porting tip: The messages at:ifAbsentPut:, at:ifPresent:, removeAllKeys:, and


removeAllKeys:ifAbsent: are not supported in Smalltalk/V or
Objectworks\Smalltalk.

Dictionary enumerating
associationsDo:, keysAndValuesDo:, keysDo:

Enumerating
collect:, conform:, detect:, detect:ifNone:, do:, inject:into:, reject:, select:

Note: The message conform: checks if each element of a collection satisfies a


condition, expressed as a one-argument block. Even though conform: is not
defined in the Blue Book, it has been included because it seems to be a
natural complement to the other Blue Book enumeration methods.

Porting tip: The conform: message is not supported in Smalltalk/V or


Objectworks\Smalltalk.

Ordered enumerating
doWithIndex:, from:to:do:, from:to:doWithIndex:, reverseDo:, with:do:

Note: The semantics of both of these operations require that the receiver collection
be ordered.

Porting tip: The doWithIndex:, from:to:do, and from:to:doWithIndex:, messages are


not supported in Smalltalk/V or ObjectWorks\Smalltalk.

Ordered removing
removeAtIndex:, removeFirst, removeLast

Rehashing
rehash

Note: It is customary for collections such as Set, Dictionary, IdentityDictionary, and


LookupTableto use the hash of elements to implement efficient access and
store operations. The use of element hash values to locate elements creates a
problem if the hash values of the elements change for some reason. For
example, if a copy of the collection is passed to another platform in a
distributed system that uses different hash functions for objects in the
collection, the collection will not function correctly. The rehash message
enables implementers to guarantee that hash-based accessing is safe.

Removing
remove:, remove:ifAbsent:, removeAll:

Chapter 2. Common Language Data Types 11


String accessing and converting
<, <=, >, >=, asLowercase, asNumber, asString, asSymbol, asUppercase,
indexOf:matchCase:startingAt:, match:, nullTerminated, sameAs:, subStrings, subStrings:

Note: Implementations of the messages <, <=, >, and >= use the U.S. English
collation sequence and do not necessarily reflect the collation sequence used
by the underlying platform. The message is needed for practical string
searching.

Porting tip: Smalltalk/V does not support match: and sameAs:. It also uses
asLowerCase and asUpperCase rather than the Blue Book (and IBM
Smalltalk) spelling asLowercase and asUppercase. Neither Smalltalk/V
nor Objectworks\Smalltalk support indexOf:matchCase:startingAt:.
Collating sequences vary from platform to platform (for example, case
sensitivity) and from language to language (for example, some
languages treat certain combinations of letters as a single character).
String comparison results can therefore differ between platforms. The
messages <, <=, >, and >= perform case insensitive comparison, while
the = message is case sensitive. This means, for example, that <= is
not the logical or of < and =. Use sameAs: instead of = for case
insensitive equality comparison.

Storing
at:put:, atAll:put:, atAllPut:, basicAt:put:, replaceFrom:to:with:,
replaceFrom:to:with:startingAt:, replaceFrom:to:withObject:

Note: The message replaceFrom:to:withObject: is not in the Blue Book but has been
included in CLDT for symmetry and completeness.

Porting tip: The method replaceFrom:to:withObject: is supported in Smalltalk/V but


not in Objectworks\Smalltalk.

Testing
includes:, isEmpty, notEmpty, occurrencesOf:, size

Unique collection protocol


In addition to the protocols supported by CLDT collection classes, several of the
collection classes have unique protocols that are not shared with any other class.

Array
multiBecome:

Note: This message is not in the Blue Book but has been included in CLDT in
order to provide an efficient means of mutating multiple objects.

Porting tip: The multiBecome: message is not supported in Smalltalk/V or


Objectworks\Smalltalk.

Bag
add:withOccurrences:

Interval
increment

Interval class
from:to:, from:to:by:

12 IBM Smalltalk: Programmers Reference


OrderedCollection
add:after:, add:afterIndex:, add:before:, add:beforeIndex:, addAll:after:, addAll:afterIndex:,
addAll:before:, addAll:beforeIndex:, addAllFirst:, addAllLast:, addFirst:, addLast:

Porting tip: The add:afterIndex:, add:beforeIndex:, addAll:afterIndex:, and


addAll:beforeIndex: messages are not supported in Smalltalk/V or
Objectworks\Smalltalk.

SortedCollection
sortBlock, sortBlock:

SortedCollection class
sortBlock:

String and DBString


addLineDelimiters, asDBString, asSBString, bindWith:, bindWith:with:,
bindWith:with:with:, bindWith:with:with:with:, bindWithArguments:, trimBlanks,
trimSeparators

Note: These String and DBStringmessages are not in the Blue Book. They have
been added to IBM Smalltalk based on feedback from developers.

Porting tip: The only one of these String and DBString messages that is supported
by Smalltalk/V is trimBlanks. Smalltalk/V also provides asInteger and
asFloat which are similar to asNumber. Objectworks\Smalltalk supports
only asNumber, byteAt:, and byteAt:put:. Objectworks\Smalltalk counts
Null (ASCII 0) as a separator in trimSeparators, but IBM Smalltalk and
Smalltalk/V do not.

Symbol
argumentCount

Note: This method is not in the Blue Book, but is needed to extract the number of
expected arguments from a selector.

Porting tip: In Objectworks\Smalltalk this method is called numArgs. It is not


included in the Smalltalk/V image.

Magnitude classes
The CLDT magnitude classes are Association, Character, Date, Float, Fraction, Integer,
and Time. Magnitude classes represent objects to which a linear ordering can be
applied, such as characters, numbers, dates, and times. Magnitude classes support
the capability to compare, query, and determine ranges of these ordered objects.

| A ScaledDecimal class also exists, and DM is its application. Although not


| technically part of CLDT, the ScaledDecimal class is associated with the magnitude
| classes of CLDT. Unlike the Float class, the ScaledDecimal class is used for exact
| representation of real numbers so that there is no round-off error.

Numbers are a major subset of the magnitude classes. The different number classes
(Float, Fraction, and Integer) provide concrete representations for integer, rational,
and real numbers. Numbers can only be created by the evaluation of an expression
or by literal declaration.

All of the magnitude classes support the basic magnitude protocol for comparing
values.

Chapter 2. Common Language Data Types 13


Magnitude comparing
<, <=, =, >, >=, between:and:, max:, min:

Association
An Association represents a binary relation between two objects, called the key and
the value.

Note: Technically, the semantics of the magnitude comparing operations are


different for Associations, because the messages <, <=, >, and >= are defined
in terms of the key only, while the = message is defined in terms of both the
key and the value. This means, for example, that <= is not the logical or
of < and =. Despite this, Association has been grouped with the magnitude
classes because that is where it is traditionally implemented in the Smalltalk
environment.

Accessing
key, key:, key:value:, value, value:

Porting tip: Both IBM Smalltalk and Objectworks\Smalltalk define the = message in
terms of both the key and the value. Smalltalk/V defines = in terms of
the key only. Smalltalk/V does not provide the message key:value:.

Creating instances (Association class)


key:value:

Character
The class Character is an abstraction for character data. Characters have values
between 0 and 65535, and are unique objects that represent code points. A code
point is simply a numeric valuethe format and graphical appearance of a
character are defined by the currently selected character set. All vendors agree that
the characters with values from 0127 correspond to the ASCII standard character
set. However, there is no agreement on the interpretation of other characters.
Groups of characters can be composed into either a String or a DBString depending
upon the values of the characters being composed.

Note: Many of the original Smalltalk implementations supported only characters


that could be represented by a single byte (characters in the range 0 to 255).
Currently, implementations of Objectworks\Smalltalk and Smalltalk/V
provide for characters whose value exceeds 255. Characters whose values
exceed 255 require two bytes of storage, and are often referred to as
double-byte characters. Double-byte character support is critical for the
development of applications for the international market place because
many languages use more than 256 characters.

Accessing
value, digitValue

Porting tip: CLDT does not support the Blue Book method asciiValue because it is
not appropriate for characters whose value exceeds 127, the range of
values converted by the ASCII standard. Rather, the asciiValue message
is replaced by the more general message value. Objectworks\Smalltalk
does not support the message value, but uses asInteger as the
equivalent message. Smalltalk/V does not support the message value,
but uses asciiValue as the equivalent message.

14 IBM Smalltalk: Programmers Reference


Converting
asLowercase, asSymbol, asUppercase, asString

Porting tip: Smalltalk/V uses the spelling asLowerCase and asUpperCase, and does
not support asSymbol. Character conversions outside the ASCII range
of values (0 to 127) vary from platform to platform and from language
to language. Character conversion results might differ between
platforms. Objectworks\Smalltalk does not support asString.

Creating instances (Character class)


digitValue:, value:

Note: The Blue Book specifies that class Character should support messages that
provide access to several of the standard ASCII nonprinting characters used
for text formatting: backspace, cr, esc, newPage, space, and tab.
Objectworks\Smalltalk adopts this approach, and extends it to support
several additional characters. The approach taken by IBM Smalltalk is to
supply all of the nonprinting ASCII characters in a pool dictionary called
CldtConstants as shown in Table 2.

Porting tip: Smalltalk/V does not support the message value:.


Table 2. The CldtConstants pool dictionary. (ASCII character values)
Key ASCII Character Value Key ASCII Character Value
Ack 6 Ff 12
Bell 7 Fs 28
Bs 8 Gs 29
Can 24 Lf 10
Cr 13 Nak 21
Dc1 17 Nul 0
Dc2 18 Rs 30
Dc3 19 Si 15
Dc4 20 So 14
Del 127 Soh 1
Dle 16 Space 32
Em 25 Stx 2
Enq 5 Sub 26
Eot 4 Syn 22
Esc 27 Tab 9
Etb 23 Us 31
Etx 3 Vt 11

Table 3. The CldtConstants pool dictionary. (LineDelimiter values)


Key Value
LineDelimiter The platform-specific line delimiter string
PMLineDelimiter The string consisting of Cr followed by Lf
WINLineDelimiter The string consisting of Cr followed by Lf

Chapter 2. Common Language Data Types 15


Testing
isAlphaNumeric, isDigit, isLetter, isLowercase, supplantation, isSeparator, isUppercase,
isVowel

Porting tip: Smalltalk/V uses the spelling isLowerCase and isUpperCase. Character
classifications outside the ASCII range of values (0 to 127) vary from
platform to platform and from language to language. Character
conversion results might differ between platforms. Neither
Objectworks\Smalltalk nor Smalltalk/V support isPunctuation for
Characters.

Date
Date represents an abstraction for a given day of a given year. The Blue Book
specifies the instance protocol of Date in functional form, but does not actually
define method names for most operations. Consequently, although all Smalltalk
vendors provide basically the same functionality, there is some variation between
the message names used. The general form is that Date provides protocol for
manipulating dates, while Date class supports instance creation and general queries.

Accessing
dayName, dayOfMonth, dayOfYear, dayIndex, monthIndex, monthName, year

Porting tip: Objectworks\Smalltalk does not support dayName or dayIndex.


Smalltalk/V supports the dayIndex message with the days of the week
numbered starting with Monday as day 1. However, because CLDT
provides National Language Support features based on the POSIX.1
standard, the CLDT dayIndex message answers Sunday as day 1.

Calculating
addDays:, asSeconds, daysFromBaseDay, daysInMonth, daysInYear, daysLeftInMonth,
daysLeftInYear, firstDayOfMonth, subtractDate:, subtractDays:

Porting tip: Objectworks\Smalltalk does not support daysLeftInMonth. Neither


Objectworks\Smalltalk nor Smalltalk/V support daysFromBaseDay for
Date.

Creating Instances
dateAndTimeNow, fromDays:, newDay:month:year:, newDay:monthIndex:year:,
newDay:year:, today

Porting tip: Neither Objectworks\Smalltalk or Smalltalk/V support


newDay:monthIndex:year:.

Querying
| daysInMonth:forYear:, daysInYear:, indexOfDay:, indexOfMonth:, nameOfDay:,
nameOfMonth:

Time
Time represents an abstraction for a time of day, based on a 24-hour clock. In
contrast to Date, class Time is much more clearly specified in the Blue Book, and
consequently there is much less variation between the vendor implementations.
Smalltalk/V adds additional protocol to Time class to control the real-time clock on
a personal computer. As with Date, the form is that Time provides protocol for
manipulating Time instances, while Time class supports instance creation and
general queries.

16 IBM Smalltalk: Programmers Reference


Accessing
hours, minutes, seconds

Calculating
addTime:, asSeconds, subtractTime:

Creating instances
dateAndTimeNow, fromSeconds:, now

Querying
millisecondsPerDay, millisecondsToRun:, millisecondClockValue

Porting tip: Neither Objectworks\Smalltalk or Smalltalk/V support


millisecondsPerDay.

Number
The number classes currently supported in CLDT are Integer, Fraction, and Float,
which provide abstractions for the mathematical concepts of integers, rational
numbers (fractions), and real numbers (floating point). The CLDT number classes
do not deal explicitly with precision or other properties of numbers that depend on
implementation. Another class, Decimal, deals explicitly with precision. Decimal
class is a subclass of Number and is defined in DecimalMath.

For example, a platform might support two or more implementations of a number


class that have identical behavior, except that they represent numbers with
different precisions or different signs. These classes would not be distinguished in
CLDT. In contrast, most other Smalltalk systems support several different integer
classes, such as SmallInteger, LargeInteger, LargePositiveInteger, and
LargeNegativeInteger. Each class is constrained regarding the size or sign of the
integers that it can represent. However, each of these classes supports the same
Integer protocol defined in CLDT.

Developers do not usually need to be aware of the distinctions between the


different representations of number classes, but at the same time the distinctions
cannot be completely ignored. Some applications depend on knowledge of the
platform-specific implementation of a number class and so are unlikely to be
portable. An example is a numerical application in which the results depend on
whether floating point numbers use single or double precision.

Porting tip: Besides these implementation issues, Smalltalk/V includes a number


of messages in the Number classes designed to support calculations for
its particular graphics and windowing system. These are not
supported in IBM Smalltalk.

All number classes support the following basic protocol:

Arithmetic
*, +, -, /, //, \\, abs, negated, quo:, rem:

Converting
@, asFloat, asFraction, asInteger, degreesToRadians, radiansToDegrees

Porting tip: Smalltalk/V does not support asFraction.

Testing
negative, positive, strictlyPositive

Chapter 2. Common Language Data Types 17


Enumerating
to:, to:by:, to:by:do:, to:do:

Generality
lessGeneralThan:, moreGeneralThan:

Note: Some developers working with numerical operations applications


occasionally need to determine the class of the return value when the
receiver and the operand have different classes. The Blue Book specifies that
the messages coerce:, generality, and retry: support explicit coercion (that is,
type conversion, or casting) between numerical classes. These messages are
used to perform the type checking required to ensure that the arguments of
primitive methods have the correct type. For more details, see Chapter 8 of
the Blue Book. In general, we believe that type checking and coercion
should be regarded as a private implementation detail, and not exposed in
application programming. lessGeneralThan: and moreGeneralThan: are
designed to supply this information without revealing any details as to how
the information is derived. In particular, there is no guarantee that number
classes support an explicit integer-valued generality.

Porting tip: Smalltalk/V and Objectworks\Smalltalk use different approaches than


IBM Smalltalk. Objectworks\Smalltalk follows and extends the Blue
Book model, adding a large number of messages designed to support
coercion and double dispatch. Smalltalk/V, on the other hand, hides
most of the type checking inside primitive methods.

Math functions
arcCos, arcSin, arcTan, cos, exp, floorLog:, ln, log:, raisedTo:, raisedToInteger:, reciprocal,
sign, sin, sqrt, squared, tan

Porting tip: Smalltalk/V does not support floorLog:, and adds the message
timesTwoPower:, which is not included in IBM Smalltalk.

Truncating and rounding


ceiling, floor, rounded, roundTo:, truncated, truncateTo:

Integer
In addition to supporting the basic protocol, the class Integer supports the
following messages.

Manipulating bits
allMask:, anyMask:, bitAnd:, bitAt:, bitInvert, bitOr:, bitShift:, bitXor:, clearBit:, highBit,
isBitSet:, noMask:, setBit:

Note: The CLDT messages clearBit:, isBitSet:, and setBit: are not in the Blue Book
but have been added to make bit-level manipulation easier.

Porting tip: Objectworks/Smalltalk does not support clearBit:, isBitSet:, or setBit. It


supports its own methods named anyBitto:, maskClear:, and maskSet:,
which are not in the Blue Book. Smalltalk/V does not support
allMask:, anyMask:, clearBit:, isBitSet:, highBit, noMask:, or setBit:.

Enumerating
timesRepeat:

Testing
even, odd

18 IBM Smalltalk: Programmers Reference


Porting tip: Smalltalk/V and Objectworks\Smalltalk define even and odd for all
numbers, not just Integer. This leads to surprising and inconsistent
results due to round-off error and other problems.

Printing
printOn:base:, printOn:base:showRadix:, printStringRadix:, printStringRadix:padTo:,
printStringRadix:showRadix:

Porting tip: All Smalltalk dialects support the message printOn:base:.


Objectworks\Smalltalk also supports printStringRadix:, which is
specified in the IBM Red Book. The additional CLDT integer printing
messages are specific to IBM Smalltalk. In some cases the other
vendors supply functionally equivalent messages.

Integer math functions


factorial, gcd:, lcm:

Converting
| asCharacter:, asScaledDecimal

Fraction
CLDT assumes that a Fraction is always reduced to its lowest common
denominator. Using unreduced instances of Fraction can cause errors with current
implementations.

Accessing
denominator, numerator

Creating instances
numerator:denominator:

Note: This message has been included to comply with the Blue Book and Red
Book, but its use is strongly discouraged. It is possible to create an
unreduced instance of Fraction with this method, which could lead to errors,
because reduced Fractions are expected. The recommended way to create a
Fraction is with the division message (/) or with conversion messages.

Converting
| asScaledDecimal

Float
Additional supported protocols for Float are:

Accessing
fractionPart, integerPart

Porting tip: The fractionPart and integerPart messages are not supported in
Smalltalk/V.

Creating instances
pi

Note: The irrational number is singled out because this message is in both the
Blue Book and Red Book, and is supported by all vendors.

Converting
| asScaledDecimal

Chapter 2. Common Language Data Types 19


ScaledDecimal
| The ScaledDecimal class represents fixed point decimal data exactly with precisions
of up to 31 digits. It also performs the arithmetic on such data when combined
with any other numeric type.

Decimals are represented as in the following example:


| 3.4s

The number is interpreted as having a value of 3.4, with a precision of 2 digits,


and a scale of 1. Precision is defined as the number of digits. Scale is defined as
the number of digits needed to represent the fractional part.

| Instances of ScaledDecimal can be used in normal Smalltalk arithmetic operations.


| Scaled Decimals are considered to be higher in generality than Integers and
| Fractions but lower than Floats. For example:
| 3.4s + 1.0 ==> 4.4 (Float)
| 3.4s + ( 1/3 ) ==> 3.73333333333333333333333333333d30.29 (Decimal)

Precisions and scales do not follow the same arithmetic rules as values. For
example:
| Precision = 3 Precision = 2 Precision = 4
| Scale = 2 Scale = 1 Scale = 2
| 9.99s + 2.3s = 12.29s

Converting
| asScaledDecimal, asFloat, asFraction, asInteger

Accessing
fractionPart, integerPart, scale, significantDigits

Printing
printOn:showDigits:Pad:

Creating instances
fromString:

| The DM application also provides the method asScaledDecimal to the class String.
| The following illustrates two ways of creating the same ScaledDecimal:
| '3.4' asScaledDecimal
| ScaledDecimal fromString: '3.4'

Graphical classes
The CLDT graphical classes are Point and Rectangle, which are abstractions
representing the plane geometrical concepts of point (two-dimensional) and
rectangle. They are described in the Blue Book. Point and Rectangle are heavily
used in the implementation of graphics routines, both in the Common Widgets and
Common Graphics subsystems, and in applications.

Point
In Smalltalk, Points are commonly written as A@B, where A and B are numbers.

Note: This proves to be a convenient denotation because it also evaluates to a


Point instance.

20 IBM Smalltalk: Programmers Reference


Smalltalk conforms to the usual convention in plane geometry in that the first, or
x-coordinate of a Point represents the horizontal dimension, and the second or
y-coordinate represents the vertical dimension.

Note: A CLDT convention is that the point 0@0 (the origin) is in the upper left
corner of the screen. The x-coordinate increases as a Point moves to the
right, and the y-coordinate increases as it moves down. In some cases this
does not conform to the usual conventions for the platform GUI (for
example, in OS/2 Presentation Manager the origin is the lower left corner).
CLDT imposes a uniform definition of origin to facilitate portability.

Accessing
x, x:, y, y:

Arithmetic
*, +, -, /, //, abs, negated

Porting tip: The arithmetic messages quo:, rem:, and \\ are defined in
Objectworks\Smalltalk, but they are not specified by the Blue Book,
and have not been included in IBM Smalltalk.

Creating instances
x:y:

Magnitude comparing
<, <=, =, >, >=, between:and:, max:, min:

Point functions
dotProduct:, dist:, normal, transpose

Note: While not often used, these messages have been included because they are
defined in the Blue Book. Objectworks\Smalltalk provides an even more
extensive library of such functions, and support for polar coordinates,
presumably to support complex computational geometry algorithms. Given
the tendency in modern systems to use platform graphics routines, or even
graphics processors, these functions have not been included in CLDT.

Porting tip: Smalltalk/V does not support dist: or normal.

Creating rectangles
corner:, extent:

Truncating and Rounding


rounded, truncated, truncatedGrid:, truncateTo:

Porting tip: Smalltalk/V does not support truncatedGrid: or truncateTo:.

Rectangle
Rectangle supports an extensive accessing protocol for getting and setting points on
the boundary of a rectangle. Most of the other messages are provided to facilitate
graphics calculations.

Porting tip: While CLDT Rectangles are similar to those defined in


Objectworks\Smalltalk, there are many differences from the
Smalltalk/V implementation. This is likely because CLDT and
Objectworks\Smalltalk are both based on the Blue Book.

Chapter 2. Common Language Data Types 21


Accessing
bottom, bottom:, bottomCenter, bottomLeft, bottomLeft:, bottomRight, bottomRight:, center,
corner, corner:, extent, extent:, height, height:, left, left:, leftCenter, origin, origin:,
origin:corner:, origin:extent:, right, right:, rightCenter, top, top:, topCenter, topLeft,
topLeft:, topRight, topRight:, width, width:

Porting tip: Objectworks\Smalltalk omits the accessors bottomLeft: and topRight:. We


include them because all the other corners had a set message, and
topRight: is specified in the Red Book. Smalltalk/V reverses the names
of the corner accessors, that is bottomLeft becomes leftBottom,
bottomRight becomes rightBottom, and so forth. Smalltalk/V does not
support the accessors (or functional equivalents) bottom:, bottomLeft:,
bottomCenter, left:, leftCenter, origin:, right:, rightCenter, top:, topCenter, or
topRight:.

Geometric functions
amountToTranslateWithin:, area, areasOutside:, contains:, containsPoint:, expandBy:,
insetBy:, insetOriginBy:cornerBy:, intersect:, intersects:, merge:, moveBy:, moveTo:,
scaleBy:, translateBy:

Porting tip: Objectworks\Smalltalk calls the scaleBy: and translateBy: messages


scaledBy: and translatedBy:. We have used the names specified by the
Blue Book and Red Book. Smalltalk/V does not support the messages
amountToTranslateWithin:, area, areasOutside:, contains:, and
insetOriginBy:cornerBy:, which are all in the Blue Book.

Creating instances (rectangle class)


left:right:top:bottom:, origin:corner:, origin:extent:

Porting tip: Objectworks\Smalltalk includes several messages that enable users to


interactively define Rectangles on the display screen. CLDT does not
support such messages, which are not consistent with Blue Book or
Red Book usage. Smalltalk/V does not support left:right:top:bottom:.

Truncating and rounding


rounded, truncated

Stream classes
The stream classes are ReadStream, ReadWriteStream, and WriteStream. Described in
Chapter 12 of the Blue Book, streams provide an abstraction that enables store and
retrieve operations to be performed on the elements of an underlying composite
data structure, usually an indexed collection. In particular, a stream remembers the
position of the last element in the data structure to be read or written. Streams are
positionable, that is, the current read/write position can be changed using the
streams public interface. This allows elements to be accessed in either a sequential
or nonsequential fashion.

It is implicit in stream semantics that the elements of the underlying data structure
are sequentially ordered. With respect to the CLDT classes, a ReadWriteStream or
WriteStream can stream over a String, DBString, ByteArray, or Array. A ReadStream
can stream over the same set of classes plus Interval, OrderedCollection,
SortedCollection, or Symbol.

Note: One significant change has been made to Blue Book streamseach CLDT
stream has an instance variable that holds a string called a line delimiter. By
far the most common use of streams in practice is to read or write String

22 IBM Smalltalk: Programmers Reference


data. Every platform uses a sequence of one or two characters to mark the
end of a line in a multiline string or text file. This character sequence is
referred to as the platform line delimiter. Platforms use different line
delimiters. For example, the AIX line delimiter is a Line Feed, while
Windows uses the sequence Carriage Return followed by Line Feed. The
platform line delimiters that are supported by CLDT are all available in the
pool dictionary CldtConstantsand are listed in Table 3 on page 15. When a
new CLDT stream is created, its line delimiter is always set to be the correct
line delimiter for the platform. However, the line delimiter can also be
changed dynamically, and it is not necessary to always use the platform line
delimiter. This allows developers to easily read or write a String obtained
from or intended for other platforms, and eliminates a serious obstacle to
portability.

The following table lists protocols supported by Stream classes.


Table 4. Protocols supported by Stream classes
Enumer- Position- Writing
Accessing Copying ating ing Reading Testing Truncating
ReadStream X X X X X X
ReadWrite- X X X X X X X X
Stream
WriteStream X X X X

Accessing
close, lineDelimiter, lineDelimiter:, size, contents

Note: As discussed above, the access methods lineDelimiterand lineDelimiter: make


it possible to read and write Strings in a portable manner by ensuring
correct semantics for the messages cr and nextLine. The Blue Book method
reverseContents was omitted, because it is equivalent to sending contents
reverse. The message close is included to support polymorphic programming
using CLDT streams and Common File System file streams. Sending close to
a CLDT stream has no effect. The message size is not in the Blue Book but
the need seems obvious.

Porting tip: The messages lineDelimiter and lineDelimiter: are not supported by
either Objectworks\Smalltalk or Smalltalk/V.

Copying
copyFrom:to:

Note: This is not a Blue Book message. However, it provides an efficient way to
copy a subset of elements from a stream without being forced to create an
intermediate collection.

Porting tip: Not supported by Objectworks\Smalltalk.

Enumerating
do:

Chapter 2. Common Language Data Types 23


Positioning
position, position:, reset, setToEnd, skip:

Reading
next, next:, nextLine, nextMatchFor:, contents, peek:, peekFor:, skipTo:, skipToAll:, upTo:,
upToAll:, upToEnd

Note: The messages skipToAll:, upToAll:, and upToEnd are not in the Blue Book but
provide needed functionality.

Porting tip: The messages skipToAll:, upToAll:, and upToEnd are not supported in
Smalltalk/V.

Testing
atEnd, isEmpty

Truncating
truncate

Note: This is not a Blue Book message but has been added because it is needed in
practice.

Porting tip: Not supported by Objectworks\Smalltalk.

Writing
flush, nextPut:, nextPutAll:, next:put:, cr, space, tab

Porting tip: The Blue Book methods crTab: and crTab: were omitted because they
can be easily composed using cr and tab. They are not included in the
Red Book. The message flush is included to support polymorphic
programming using CLDT streams and CFS file streams. Sending flush
to a CLDT stream has no effect.

Creating instances
ReadStream class supports the Blue Book instance creation messages on: and
on:from:to:. WriteStream class supports the Blue Book messages on:, with:, and
with:from:to:. Instances of ReadWriteStream can be created by sending any of the
above messages to ReadWriteStream class.

Support classes
The support classes are UndefinedObject, Message, DirectedMessage, and Block.

UndefinedObject
The class UndefinedObject is discussed in Chapter 14 of the Blue Book. It describes
the behavior of a special object called nil, which is used to represent the value of
an unassigned variable or an undefined result. There is only a single instance of
UndefinedObject in the system. The messages isNil and notNil, inherited from Object
protocol, are redefined in UndefinedObject to always return the values true and false.
In addition, nil supports a protocol to create new Smalltalk classes, and to permit
the creation of new classes that have no superclass.

24 IBM Smalltalk: Programmers Reference


Message and DirectedMessage
The concept of a Message class is briefly discussed in the Blue Book, but no details
are provided about its protocol. CLDT defines both a Message class and a
DirectedMessage class. Both are used in error handling and to support
communication between the virtual machine and the rest of the system. A Message
represents a Smalltalk message. It supports the basic accessors arguments,
arguments:, selector, and selector:. A DirectedMessage is a Message that also knows
about its receiver. It supports the basic accessors plus receiver, receiver:, and send.

Porting tip: In Objectworks\Smalltalk a DirectedMessage is called a MessageSend.


Smalltalk/V has no equivalent of Message, but has a class Message that
is equivalent to DirectedMessage. Messages are created using the class
message new. DirectedMessages in IBM Smalltalk can be created by
sending the class messages new and selector:arguments:receiver:.

Block
A Block represents a lexical closure, that is, a piece of compiled code that can be
run on demand and need not be associated with a message in any class. Instances
of Block support two messages, whileTrue: and whileFalse:, which are used to
construct while loops. Additional Block protocol is defined in Exception handling
classes, Block evaluation methods on page 48, and Process-related block
methods on page 49.

Exception handling classes


The exception handling classes, ExceptionalEvent and Signal, provide an elegant
means of handling exceptional events and error conditions.

ExceptionalEvent
Instances of this class represent an exceptional condition. Instances are signaled to
cause an exception handler to be invoked.

Signal
Instances of this class represent a signaled exception and are passed as an
argument to the exception handler.

Tip: As with the goto: statement of conventional languages, this can create
problems with code maintenance, and it is therefore recommended that
exception handling only be used for handling exceptional circumstances or
error conditions.

Creating new exceptions


Exceptions are instances of the class ExceptionalEvent and are created by sending
the message newChild to an existing, or parent, exception. The most general
exception is an instance of ExceptionalEvent called ExAll, which has no parent. All
other exceptions are children of exactly one other (more general) exception. This
creates a hierarchy of exceptions. Instances of ExceptionalEvent maintain the
following state:
description
A String that describes the general circumstances where the exception
arises (the error message).
parent The exceptions parent, nil for ExAll.

Chapter 2. Common Language Data Types 25


resumable
A Boolean that is true if running can be resumed at the place where the
exception was signaled. It is copied from parent to child on creation, and is
false for ExAll.
defaultHandler
A one-argument block that is fired if the exception is not explicitly
handled. The default handler block is passed the instance of Signal that
describes the exception that was signaled. If nil, the parents defaultHandler
is applied. The defaultHandler in ExAll sends error:.

The following examples create a couple of types of exception:

Example: creating an end of file exception


| anEndOfFileException |
(anEndOfFileException := ExAll newChild)
description: 'end of file'.

Example: creating a message not understood exception


| aMessageNotUnderstoodException |
(aMessageNotUnderstoodException := ExAll newChild)
description: 'message not understood'.

Signaling an exception
Exceptions are signaled by sending one of the following methods to the
appropriate instance of ExceptionalEvent:
signal Invokes the exception with no arguments.
signalWith:
Invokes the exception with a single argument.
signalWith:with:
Invokes the exception with two arguments.
signalWithArguments:
Invokes the exception with anArray of arguments

The following examples signal a couple of types of exception:

Example: signaling end of file


| anEndOfFileException |
(anEndOfFileException := ExAll newChild)
description: 'end of file'.
anEndOfFileException signal.

Example: signaling message not understood


| aMessageNotUnderstoodException |
(aMessageNotUnderstoodException := ExAll newChild)
description: 'message not understood'.
aMessageNotUnderstoodException
signalWith: (DirectedMessage selector: #halt arguments: #() receiver: self)

When an exception is signaled, an instance of class Signal is created that contains


information describing the circumstances where the exception occurred.

Handling of exceptions
You construct exception handlers by sending the message when:do: to a block. You
can install multiple handlers simultaneously by using repeated forms of when:do:
(that is, when:do:when:do:, when:do:when:do:when:do:, and so on) for up to five

26 IBM Smalltalk: Programmers Reference


handlers. Because the handlers are tried in order, more general handlers should be
placed later in the list. You can use nesting to allow more than five handlers if
absolutely required.

The first argument to when:do: is an instance of class ExceptionalEvent. The second


argument is a one-argument handler block that is passed the instance of Signal
that describes the exception.

When an exception is signaled, the most recent when:do: matching the exception is
evaluated first. When a when:do: message is found with either the exception or a
more general exception (one of the exceptions ancestors) as the when argument, its
handlerBlock is run.

Within the handler block, the following methods can be sent to an instance of
Signal:
argument
Answers the first argument that was passed to the signal message, or nil if
none.
arguments
Answers a collection containing any arguments that were passed to the
signal message.
exitWith:
Immediately returns an object instance as the value of the when:do:
exception.
resumeWith:
Returns key and value as the result of the signal message.
signal Indicates that the current handler is finished handling, or is not going to
handle, the exception and that a search for another handler should begin.
Notice that this is not the same semantics as sending signal to an instance
of ExceptionalEvent.
signalWith:
Same as signal, but modifies the arguments which are passed on.
signalWith:with:
Same as signal, but modifies the arguments which are passed on.
signalWithArguments:
Same as signal, but modifies the arguments which are passed on.
retry Runs the block that the when:do: message was sent to. All handlers are
reinstated.
handlesByDefault
Runs the default handler for the exception.
description
Answers a description of the exception. Returns the string an exception
has occurred if no description was set in the ExceptionalEvent.
exception
Answers the exception that was signaled. Access to the exception is not
normally required by handlers. However, this method is provided for
special purpose applications.

Assuming that ThatException is a global variable, an application could have the


following code:

Chapter 2. Common Language Data Types 27


"Initialization code."
(ThatException := ExAll newChild)
description: 'That exception occurred.'.
thatMethod
"Answer true if thatTest completes without error,
otherwise signal ThatException."
self thatTest failed
ifTrue: [ ThatException signal ].
|true

Note: The application has determined that an exceptional condition has occurred
and signals an exception.
anyMethod
"The application can then do something like the following.
Answer true if thatMethod completes without exception,
and false otherwise."
|[ self thatMethod ]
when: ThatException
do: [:signal |
"Handle the exception in some way: increment a counter,
print a message, fix a problem, ... and then exit the handler."
signal exitWith: false].

Note: From do: [:signal to the end is the handler block.

Note that dropping off the end of a handler block is equivalent to sending
resumeWith: (Association
key: #resume
value: valueOfTheBlock

where valueOfTheBlock is the value of the handler block.

Note: Neither the handler block nor the block that receives when:do: can contain a
return (|) expression. Although this is not a significant limitation, it should
be kept in mind when automatic code generation techniques are used,
because arbitrary code fragments cannot be wrapped with exception
handlers without first checking for return expressions.
v It is possible to signal another exception within the body of the handler.
v The handler block should not contain a return (|) expression. Returning
from an exception handler can cause unpredictable results.

ExceptionalEvent collections
Occasionally, it is desirable to use a single handler for several nonhierarchically
related exceptions. To handle this situation, the when:do: syntax has been extended
to allow an ExceptionalEvent collection as the when argument. ExceptionalEvent
collections are created by sending the vertical bar (|) message to an instance of
ExceptionalEvent as follows:

In class ExceptionalEvent:
| anExceptionalEvent
"Answer a new ExceptionalEvent collection
containing the receiver and anExceptionalEvent."

In the ExceptionalEvent collection:


| anExceptionalEvent
"Add anExceptionalEvent to the exceptions stored in the receiver."

Given these definitions, it is possible to write the following:

28 IBM Smalltalk: Programmers Reference


[ "Some code that could cause several exceptions" ]
when: Type1Exception | Type2Exception | Type3Exception
do: [ :signal | signal exitWith: 'I expected this exception'].

ExceptionalEvent collections can be stored in a variable and later referred to:


ExpectedExceptions :=
Type1Exception | Type2Exception | Type3Exception.
[ "Some code that could cause several exceptions"[
when: ExpectedExceptions
do: [ :signal | signal exitWith: 'I expected this exception'].

ExceptionalEvent collections cannot be signaled.

Within the body of a handler block, sending exception to the passed-in instance of
Signal always answers the particular exception that was signaled.

Completion blocks
The syntax codeBlock whenExceptionDo: completionBlock runs the completion block if
any exception occurs during the running of the code block. The completion block
is not considered to have handled the exception. That is, a further exception handler
is looked for after the completion block is run.
codeBlock whenExceptionDo: completionBlock

is equivalent to:
codeBlock
when: ExAll
do: [ :signal |
completionBlock value.
signal resumeBlock: nil
signal signal ].

The syntax codeBlock atEndOrWhenExceptionDo: completionBlock causes the


completion block to be run whether an exception occurs during the running of the
code block, or the code block successfully runs to completion. As above, the
completion block is not considered to have handled the exception. That is, a
further exception handler is looked for after the completion block is run.
codeBlock atEndOrWhenExceptionDo: completionBlock

is equivalent to:
| cleanedUp |
cleanedUp := false.
codeBlock
when: ExAll
do: [ :signal |
completionBlock value.
cleanedUp := true.
signal resumable: false.
signal signal ].
cleanedUp ifFalse: [ completionBlock value ]

Note that completion blocks are not passed any arguments.

Note: After a completion block has been run because of an exception, the
exception (actually the instance of Signal) is marked as being nonresumable
before another handler is searched for. This prevents the completion block
from being run multiple times.

Chapter 2. Common Language Data Types 29


Default exception handler
Instances of ExceptionalEvent can have a default handler block that is run if the
exception occurs outside of a when:do expression.
| anException |
(anException := ExAll newChild)
defaultHandler: [:signal |
self error: 'The exception was not expected at this time.'].
anException signal.

If desired, the default handler block can be run from inside of a handler block by
sending handlesByDefault to the handlers signal argument.

Note: The exitWith: and retry messages cannot be used in the default handler block
unless the expression that caused the exception was run inside the receiver
block of a when:do: expression.

System exceptions
Globally accessible exceptions are stored in the pool dictionary named
SystemExceptions. By convention, the names of the exceptions in this pool begin
with Ex. SystemExceptions is a pool dictionary belonging to Object and is therefore
accessible by all subclasses of Object.

The following table lists system exceptions in the SystemExceptions pool dictionary.
Table 5. SystemExceptions pool dictionary
Pool variable name Description
ExAll The most general exception.
ExError The exception signaled by Object>>error:. This exception is
signaled with the string passed to error:.
ExHalt The exception signaled by Object>>halt. This exception is
signaled with the string passed to halt:. Note that Object>>halt
sends halt:.
ExUserBreak The exception signaled by the system default break handler.

The system exceptions ExError, ExHalt, and ExUserBreak use the first argument for
the error string when opening a debugger. If you create a child of these exceptions,
you must follow the same convention. Alternatively, you can provide a
defaultHandler in the child, which sends error: with an appropriate error message.
The defaultHandler for the system exceptions uses the description of the exception
as the error string if the first argument is not a string. If you are using exceptions
to return non-string values in the arguments of the signal (a reasonable use) then
you should provide a default handler for your exceptions that knows that the
arguments will not be strings. For example:
| anErrorException |
(anErrorException := ExError newChild)
description: 'a demonstration error occurred';
defaultHandler: [:sig |
self error: 'My error occurred with arguments: ',
sig arguments printString].

anErrorException signalWith: 1 with: 2.

Examples of exception handling


The follow examples show how to handle various situations:

30 IBM Smalltalk: Programmers Reference


Example: printing an object
"Assume that printString will call self error: if it encounters an error condition."
| anObject |

[ anObject printString ]
when: ExError
do: [:signal |
signal
exitWith: 'Exception: ', signal argument, 'while printing
a ', anObject class name ].

Example: trapping user break


[ "Unbreakable code"
1000 timesRepeat: [String new: 100000]]
when: ExUserBreak
do: [:signal |
System confirm: 'Caught user break'.
signal resumeWith: nil ].

Example: growing a stack


"Assume that a Stack class with a push: method exists in the application."
(OverflowException := ExAll newChild)
description: 'stack overflow'.
[aStack push: anInteger]
when: OverflowException
do: [ :signal |
aStack grow.
signal retry ].

Example: propagating a different exception


| aDifferentException |
(aDifferentException := ExAll newChild)
description: 'this is a different exception'.

[1 error: 'demonstration' ]
when: ExError
do: [:signal |
aDifferentException signal].

Example: top-level loop


| loopExitException |
loopExitException := ExAll newChild.

[ [ true ] whileTrue: [
"Body of loop. Can only be exited by saying
'loopExitException signal'."
(System confirm: 'Signal the loopExitException')
ifTrue: [loopExitException signal].

"Create a doesNotUnderstandException."
1 error: 'This is for demonstration purposes'.
]
]
when: loopExitException
do: [ :signal |
signal exitWith: 'bye' ]
when: ExAll
do: [ :signal |
System message: 'An Exception has occurred: ', signal description.
signal retry ].

Chapter 2. Common Language Data Types 31


Example: close stream at end or on error
"If an error occurs, it is reported normally, but aStream is closed first."
| aStream |

aStream := ReadStream on: 'This is a test'.

[ [ aStream atEnd ]
whileFalse: [ Transcript nextPut: aStream next. ]
]
atEndOrWhenExceptionDo: [
Transcript show: '...Closing the stream'.
aStream close ].

32 IBM Smalltalk: Programmers Reference


Chapter 3. Common Language Implementation
The Common Language Implementation (CLIM) subsystem provides a
platform-independent interface that is primarily intended to describe the shared
behavior of IBM Smalltalk classes. A class in Smalltalk is an abstraction that
describes a kind of object. We have already encountered a large number of IBM
Smalltalk classes in the previous section on CLDT: Integer, SortedCollection, Date,
and so on. One of the fundamental symmetries of the Smalltalk environment is
that every object is an instance of a class. That includes classes themselves, which
are instances of special classes called metaclasses.

Unless you are already an experienced Smalltalk programmer, both the idea of
metaclasses and the role that they play in the language are probably unclear. The
Blue Book contains a discussion that might help to sort out the subject. The good
news is that most developers, even those who write very sophisticated Smalltalk
programs, never have to deal explicitly with metaclasses. They exist only in the
background, part of the hidden machinery that makes things work.

CLIM is intended to be used by application developers who are implementing


Smalltalk development tools. CLIM complements the messages described in CLDT,
most of which define either instance behavior, or class behavior that is specific to
one or a few related classes. CLIM consists of the concrete classes Class, Metaclass,
Compiler, ClimCompilerError, CompiledMethod, and EmSystemConfiguration. In
addition, it adds several messages for creating classes to the CLDT class
UndefinedObject, and extends the classes Array, String and DBString. In the same
way that it is convenient to include the abstract class Object in CLDT, we have also
included the class Behavior in CLIM. Behavior describes a number of messages that
are shared by Class and Metaclass, and through inheritance, by all of the classes in
IBM Smalltalk. The class hierarchy is:

Behavior
ClassDescription
Class
Metaclass

CLIM does not provide a complete definition of Class or Metaclass. What it does do
is define a number of common class messages that are supported by all classes in
IBM Smalltalk. As might be guessed, CLIM has rather specific objectives. It is
intended to support:
v Browsing
v Creating or modifying class structures
v Compilation
v Adding and deleting methods to classes (with some restrictions)

It is not intended to support:


v Meta-level programming
v Writing compilers, image builders, or other tools that depend on, or modify, the
fundamental mechanisms of Smalltalk

It is expected that developers who want to add, modify, or delete methods to IBM
Smalltalk classes will normally do so using the source code management facilities.
However, there are cases where it might be necessary to add or delete methods
outside the control of the source code manager. An example might be behavior that

Copyright IBM Corp. 1994, 2000 33


is defined by an application at run time, compiled into a method that is added to
some class, run, and then deleted. (However, CLIM classes are not included in the
runtime environment.) This is how the Execute command in text menus is often
implemented. CLIM provides a minimal set of messages intended to address this
specific requirement. It does not, however, include any messages that store source
code. Developers are cautioned that when they are adding or deleting methods
outside the control of the source code manager, it is their responsibility to leave the
system in a well-defined and consistent state.

Behavior messages
The messages described in this section provide access to the properties and
attributes of a class, including the methods it defines. Behavior messages are
mainly intended to support browsing by allowing developers to perform queries
over class structures.

Class queries
allSubclasses, allSuperclasses, inheritsFrom:, name, subclasses, superclass, symbol,
whichClassIncludesSelector:, withAllSubclasses, withAllSuperclasses

Note: The Blue Book defines all of these messages except symbol and
withAllSuperclasses. We added symbol to provide a platform-independent
message that returned a symbol equal to the class name, and
withAllSuperclasses because it is the obvious complement of withAllSubclasses.

Porting tip: Smalltalk/V does not support whichClassIncludesSelector:, and it returns


a string when name is sent. Objectworks\Smalltalk returns a symbol
when name is sent, and does not support symbol.

Tip: If name is sent to a class, a Symbol is answered. If name is sent to a metaclass,


a String is answered.

Compiling
compiler

Note: This message returns the compiler that is appropriate for a particular class.
There might be more than one compiler supported by the system, and each
class needs a way to find its own.

Porting tip: Not supported by Smalltalk/V and Objectworks\Smalltalk, which both


provide a similar message called compilerClass. Both Smalltalk/V and
Objectworks\Smalltalk provide a set of compilation messages for
classes, based loosely on the Blue Book. We have not included such
messages in this release, because they intrude on source code
management. The usual way to compile methods and add code to the
database for developers using IBM Smalltalk is to use the facilities
provided by IBM Smalltalk.

Creating instances
basicNew, basicNew:

Note: The standard messages used in Smalltalk to create new instances of classes
are new, basicNew, new:, and basicNew:. These messages are defined in
Behavior in the Blue Book, but this causes them to be inherited by many

34 IBM Smalltalk: Programmers Reference


classes for which one or more are not appropriate. CLIM and CLDT take a
more careful approach. Only the message basicNew is defined for all classes.
As specified in the Blue Book, basicNew and basicNew: are intended to be the
primitive object creation messages and are not intended to be overridden,
because the other instance creation messages are often defined in terms of
this one. Other instance creation messages are inherited as specified in the
class messages for each CLDT and CLIM class.

Enumeration
allMethodsDo:, methodsDo:, allSubclassesBreadthFirstDo:, allSubclassesDepthFirstDo:,
allSubclassesDo:, allSuperclassesDo:, withAllSubclassesBreadthFirstDo:,
withAllSubclassesDepthFirstDo:, withAllSubclassesDo:, withAllSuperclassesDo:

Note: The Blue Book defines only the messages allSubclassesDo: and
allSuperclassesDo:. These messages do not provide any control over the order
in which classes are traversed, which is important for some applications.
The messages prefixed by with include the receiver class in the classes that
are being enumerated.

Porting tip: None of these messages are supported in Smalltalk/V.


Objectworks\Smalltalk supports only allSubclassesDo:.

Instance accessing
allInstances, basicAllInstances

Note: allInstances is a Blue Book message. It is defined in IBM Smalltalk to return


all live instances of a class. Live instances are those that are being
referenced by another (referenced) object, and so will not be reclaimed by
the next pass of the garbage collector. (Garbage collection is a Smalltalk
process for periodically identifying unreferenced objects and deallocating
their memory.) Sending allInstances usually forces a garbage collection to
ensure that only live instances are returned. The message basicAllInstances is
not in the Blue Book, but experience indicates it is necessary. It is generally
faster because it does not require the garbage collection; however, all of the
returned instances are not guaranteed to exist.

Porting tip: Neither Smalltalk/V nor Objectworks\Smalltalk supports


basicAllInstances, although Smalltalk/V does have a message with
similar semantics called allInstancesPrim. Smalltalk/V supports
allInstances. Objectworks\Smalltalk also has a message named
allInstances, but it has the semantics of basicAllInstances.

Instance structure testing


instSize, isBits, isBytes, isFixed, isPointers, isVariable

Note: These messages are all defined in the Blue Book.

Porting tip: Objectworks\Smalltalk does not support isBytes.

Method accessing
>>, allSelectors, compiledMethodAt:, compiledMethodAt:ifAbsent:, methodDictionary,
selectors, sourceCodeAt:, sourceCodeAt:ifAbsent:

Chapter 3. Common Language Implementation 35


Note: The messages allSelectors, compiledMethodAt:, selectors, and sourceCodeAt: are
defined in the Blue Book. Note that methodDictionary has been carefully
defined so it does not imply that a real method dictionary is used to store
methods. Requiring an actual method dictionary seems to be an
unreasonable restriction on implementors. For the same reason we have
omitted the Blue Book message methodDictionary:.

Porting tip: Smalltalk/V supports only compiledMethodAt:, selectors, and


sourceCodeAt:. Smalltalk/V uses multiple method dictionaries and
defines the messages methodDictionaries and methodDictionaries: to
support them. Objectworks\Smalltalk supports all messages except the
accessors >>, sourceCodeAt:ifAbsent:, and methodDictionary, although it
does support the corresponding set method methodDictionary:,
following the Blue Book in this practice.

Method adding and deleting


addCompiledMethod:, deleteAllSelectors:, deleteSelector:, deleteSelector:ifAbsent:

Note: The Blue Book messages addSelector:withMethod: and removeSelector: are


integrated with source code management systems on most platforms.
Changing the semantics of these messages to remove this dependency
would have broken too much existing code. We decided instead to add new
messages that are neutral regarding source code management; that is, they
are intended to exist outside the source code management system.

Porting tip: Neither Smalltalk/V nor Objectworks\Smalltalk supports these


messages.

Tip: Using addCompiledMethod:, deleteAllSelectors:, deleteSelector:, or


deleteSelector:ifAbsent: might make the image inconsistent with the source code
management system. These methods should only be sent by experienced
developers who are certain of the outcome. Objectworks\Smalltalk support
these messages.

Method queries
allMethodsNamed:, allMethodsReferencingInstVarName:, allMethodsReferencingLiteral:,
allMethodsSending:, allMethodsSendingAll:, canUnderstand:, hasMethods,
includesSelector:, whichMethodsReferenceInstVarName:, whichMethodsReferenceLiteral:,
whichMethodsSend:, whichMethodsSendAll:, whichMethodsReferenceInstVarName:,
whichMethodsReferenceLiteral:, whichMethodsSend:, whichMethodsSendAll:

Note: The Blue Book only defines the messages canUnderstand:, hasMethods, and
includesSelector:. We decided to provide a richer set of method query
messages so that it would be possible to hide implementation details
without losing functionality.

Porting tip: Smalltalk/V only supports canUnderstand: and includesSelector:, as well


as several messages designed to query methods about instance and
class variable references. Objectworks\Smalltalk supports only the Blue
Book messages canUnderstand:, hasMethods, and includesSelector:.

Class messages
The class Class describes the representation and behavior of objects.

36 IBM Smalltalk: Programmers Reference


Class variable accessing
addClassVarName:, allClassVarNames, classPool, classVarNames, removeClassVarName:,
setClassPool:

Note: These are all Blue Book messages except setClassPool:. The message
setClassPool: is a basic accessor that works outside the source code manager.

Porting tip: All supported by both Smalltalk/V and Objectworks\Smalltalk except


setClassPool:.

Instance variable accessing


addInstVarName:, allInstVarNames, instVarNames, removeInstVarName:

Note: These are all Blue Book messages. Note that it is not possible to add
instance variables to variable byte classes.

Porting tip: Smalltalk/V does not support addInstVarName: and


removeInstVarName:.

Shared pool accessing


addSharedPoolName:, allSharedPoolNames, removeSharedPoolName:, setSharedPoolNames:,
sharedPoolNames

Note: The Blue Book defines the messages addSharedPool:, allSharedPools,


removeSharedPool:, and sharedPools, but it takes an inconsistent approach to
identifying the shared pool dictionary, referencing it sometimes by name and
other times by value. We decided to standardize on reference by name, and
appended the suffix Name to the messages to reinforce this point. The
aggregate message setSharedPoolNames: is a basic accessor that works outside
the source code manager. There is no Blue Book equivalent to
setSharedPoolNames:.

Porting tip: Not supported by Smalltalk/V or Objectworks\Smalltalk. Smalltalk/V


provides instead the messages addSharedPool:, removeSharedPool:,
sharedPool, and sharedPool:. Objectworks\Smalltalk provides the Blue
Book messages noted above in the Rationale; however, it changes the
Blue Book semantics to always use reference by value.

Tip: Using setSharedPoolNames: will make the image inconsistent with the source
code management system. This method should only be sent by experienced
developers who are certain of the outcome.

Class accessing
comment, comment:, definitionString, setClassName:

Note: The message setClassName: is a basic accessor that works outside the source
code manager. No renaming messages aside from setClassName: are included
in this release of CLIM. Under normal circumstances renaming should be
under the control of the source code manager.

Porting tip: Smalltalk/V and Objectworks\Smalltalk both support comment and


comment:. Objectworks\Smalltalk has a message named definition that is
the same as definitionString, while Smalltalk/V has no such message.

Chapter 3. Common Language Implementation 37


Neither supports setClassName:. Both Smalltalk/V and
Objectworks\Smalltalk provide other messages for renaming classes.

Tip: Using setClassName: will make the image inconsistent with the source code
management system. This method should only be sent by experienced
developers who are certain of the outcome.

Initializing and removing


initialize, removeFromSystem

Note: The message removeFromSystem is integrated with the source code manager,
and therefore safe to use.

Superclass accessing
connectToSuper, disconnectFromSuper

Note: These messages both operate outside the scope of the source code manager.

Porting tip: Smalltalk/V and Objectworks\Smalltalk provide other messages that


have the same effect.

Tip: Using connectToSuper or disconnectFromSuper will make the image inconsistent


with the source code management system. This method should only be sent
by experienced developers who are certain of the outcome.

Metaclass messages
The class Metaclass provides protocol for accessing the metaclasss primary
instance.

Accessing
primaryInstance, primaryInstance:

Note: Needed to access the class that is the primary instance of the metaclass.

Porting tip: Not supported in Smalltalk/V. Objectworks\Smalltalk provides the


accessor only, calling it soleInstance rather than primaryInstance.

Creating new classes


CLIM defines several messages for creating new classes. A class is usually created
by defining it as a subclass of an existing class; in that case, the class creation
message is sent to the superclass. This message actually results in two classes being
added to the system, not one. First, a metaclass is created that describes the new
class, and then the metaclass is instantiated to create the class itself.

A few restrictions apply when creating new classes. Any subclass of a variable
class, that is, a class like Array that has indexed instance variables, must also be a
variable class. Similarly, any subclass of a variable byte class, that is, a class like
String that has indexed instance variables that are bytes, must also be a variable
byte class. Variable byte classes cannot have named instance variables, and any
class with a named instance variable cannot have variable byte subclasses.

38 IBM Smalltalk: Programmers Reference


Sometimes developers need to create classes that are not subclasses of an existing
class. To support this, the special object nil, described in the previous section on
CLDT, is extended so that it also understands the class creation messages.

We refer to a class that has no indexed instance variables, that is, only named
instance variables, as a fixed class.

Creating fixed classes


subclass:classInstanceVariableNames:instanceVariableNames:classVariableNames:poolDictionaries:,
subclass:instanceVariableNames:classVariableNames:poolDictionaries:

Note: These messages can only be sent to another fixed class.

Porting tip: Smalltalk/V supports the second message, but not the first, while
Objectworks\Smalltalk supports neither. Neither Smalltalk/V nor
Objectworks\Smalltalk provide Class messages for creating classes with
class instance variables, although it is possible to build such classes.
Objectworks\Smalltalk uses different subclass creation messages that
also specify the category.

Creating variable classes


variableSubclass:classInstanceVariableNames:instanceVariableNames:classVariableNames:-
poolDictionaries:,
variableSubclass:instanceVariableNames:classVariableNames:poolDictionaries:

Note: These messages can only be sent to a fixed class or another variable class.

Porting tip: Smalltalk/V supports the second message, but not the first, while
Objectworks\Smalltalk supports neither. Neither Smalltalk/V nor
Objectworks\Smalltalk provide Class messages for creating classes with
class instance variables, although it is possible to build such classes.
Objectworks\Smalltalk uses different subclass creation messages that
also specify the category.

Creating variable byte classes


variableByteSubclass:classInstanceVariableNames:classVariableNames:- poolDictionaries:,
variableByteSubclass:classVariableNames:poolDictionaries:

Note: These messages can only be sent to another variable byte class.

Porting tip: Smalltalk/V supports the second message, but not the first, while
Objectworks\Smalltalk supports neither. Neither Smalltalk/V nor
Objectworks\Smalltalk provide Class messages for creating classes with
class instance variables, although it is possible to build such classes.
Objectworks\Smalltalk uses different subclass creation messages that
also specify the category.

Extended classes
CLIM extends the classes Array, String, and DBstring.

Chapter 3. Common Language Implementation 39


Multiple instance accessing
Multiple Instance Accessing

The following methods are Array instance methods.

basicMultiAllInstances, MultiAllInstances

Note: Neither method is in the Blue Book, but they have been included in CLIM
as an efficient way of accessing multiple instances.

Porting tip: Neither Smalltalk/V nor Objectworks support multiAllInstances or


basicMultiInstances.

String converting
The following methods are String and DBString instance methods.

asClassPoolKey, asGlobalKey, asPoolKey

Note: The messages asClassPoolKey, asGlobalKey, and asPoolKey are not in the Blue
Book. They have been added to IBM Smalltalk to provide developers with a
way to ensure that class variables, global variables, and pool dictionary keys
have the class that is correct for the platform (usually either String or
Symbol). IBM Smalltalk will not accept class variables, global variables, or
pool dictionary keys containing characters whose value exceeds 255;
consequently asClassPoolKey, asGlobalKey, and asPoolKey messages will not be
useful if sent to a DBString that contains these characters.

Porting tip: Neither Smalltalk/V nor Objectworks\Smalltalk support asClassPoolKey,


asGlobalKey, or asPoolKey.

Compiling and evaluating code


Although the Blue Book describes the Smalltalk compiler, it does not actually
specify any of the messages that it should support. The various Smalltalk dialects
are not consistent on whether to access the compiler through Compiler class
methods, or to create a compiler instance and access compilation facilities through
instance methods. IBM Smalltalk takes the first approach, but the choice is
completely arbitrary.

A key question is how to handle compile errors. CLIM defines an object that
encapsulates all of the error information that a compiler might return. A
CompilerError is required to support the following accessor messages, each of which
returns one of the attributes of the error object:
context Answers the class in whose context the source code was compiled
message
Answers the compiler error or warning message
sourceString
Answers the source code string that was compiled
startPosition
Answers the position in the source code string of the first character of the
token at which the error or warning was detected

40 IBM Smalltalk: Programmers Reference


stopPosition
Answers the position in the source code string of the last character of the
token at which the error or warning was detected

Compiler error handling in CLIM works as follows:


1. Several of the class messages defined for Compiler accept as a parameter a
one-argument block, called the fail block.
2. If the compilation is successful, these messages return an instance of
CompiledMethod, described in the next section.
3. However, if a compiler warning or error occurs, a CompilerError describing the
error is created.
4. It is then passed as an argument to the fail block, which is evaluated.
5. The result of evaluating the fail block is returned.

Note that CLIM does not define how the CompilerError is created, or how its
attributes are set. These are assumed to be platform-specific operations. This
mechanism was chosen because it provides a fairly general way to handle
warnings and errors, but does not require a complex runtime infrastructure.

The compilation facilities provided in CLIM support two distinct activities:


compiling and evaluating.

Compiling
compile:inClass:, compile:inClass:ifFail, compile:inClass:warningLevel:onWarning:ifFail:

Note: Each of these messages compiles the source code provided in the first
parameter in the context of the second parameter, which must be a class. To
compile in the context of a class means that the compiler will try to interpret
variable references as instance, class, or pool variables of the designated
class. These messages return a CompiledMethod if they are successful. The
compile:inClass: message uses default error handling that returns nil if a
warning or error is detected. The compile:inClass:ifFail: message accepts a fail
block as the third parameter, and answers the result of evaluating the fail
block if a warning or error is detected. The
compile:inClass:warningLevel:onWarning:ifFail: message accepts a warning
level, warning block, and fail block as parameters. If an error is detected, the
result of evaluating the fail block as answered. If a warning is detected, the
warning block is evaluated with an instance of CompilerError. If that answers
true, the compile fails, and the CompilerError is answered. Otherwise the
compile continues.

Porting tip: Not supported in Smalltalk/V and Objectworks\Smalltalk, which each


provide their own different messages to support compiling.

Evaluating
evaluate:, evaluate:for:, evaluate:for:ifFail:, evaluate:for:warningLevel:onWarning:ifFail:

Note: Each of these messages evaluates source code in the context of an object. To
evaluate in the context of an object means that the compiler will try to interpret
variable references as instance, class, or pool variables of the designated
objects class, and that instance variable references is bound to the values of
the objects instance variables when the compiled code is run. The first
parameter of each of these messages is always the source code to be
evaluated. The message evaluate: evaluates in the context of the special object

Chapter 3. Common Language Implementation 41


nil. Error handling follows the same model as for compiling, that is, if a
warning or error is detected during compilation, either a CompilerError or
the result of evaluating a fail block is returned.

Porting tip: Smalltalk/V and Objectworks\Smalltalk each support evaluate:, but


otherwise they provide their own different messages to support
evaluating.

CompiledMethod
A CompiledMethod is an abstraction for compiled code. CompiledMethod is described
in the Blue Book, but no protocol is actually defined for them. The exact
representation of a compiled method is, of necessity, platform-specific. CLIM
defines only those basic accessing and testing messages that do not need to make
any assumptions about structure or representation.

Accessing
methodClass, methodClass:, selector, selector:, sourceString, symbolLiterals

Porting tip: Smalltalk/V supports only selector, selector:, and sourceString.


Objectworks\Smalltalk does not support any of these messages, but
does provide equivalent ways to access the same information.

Testing
equals:, isPrimitive, isUserPrimitive, referencesLiteral:, sendsSelector:, getsInstVar:,
setsInstVar:, referencesInstVar:

Porting tip: Smalltalk/V does not support any of these messages.


Objectworks\Smalltalk supports only sendsSelector:, although it also has
a message called refersToLiteral: that is similar to referencesLiteral:. Both
Smalltalk/V and Objectworks\Smalltalk redefine =. We decided to
define an equals: message with the correct semantics instead.

EmSystemConfiguration
Each IBM Smalltalk system includes a single instance of the class
EmSystemConfiguration, accessed through the global variable System. System serves
two functions: It provides access to useful information about how the image is
configured, and it also accepts messages to save the image and shut down the
system.

The current system configuration can be obtained by sending System the message
configuredSubsystems. This returns a dictionary showing which variant of each IBM
Smalltalk subsystem is configured, keyed by the subsystem name. The keys must
include one of the following strings: CLDT, CLIM, CPM, CFS, CG, CW, or
CP.

Typical values for each subsystem key are shown in the following table. This table
might not be complete. Other values might be supported in subsequent releases.
Table 6. Typical values for subsystem keys
Key Typical values
CLDT ES
CLIM ES

42 IBM Smalltalk: Programmers Reference


Table 6. Typical values for subsystem keys (continued)
Key Typical values
CPM ES
CFS POSIX, OS/2, WIN32s
CG X, PM, WIN32s
CW MOTIF, PM, WIN32s
CP PM, WIN32s

If a platform is not supported, the value for its key is UNSUPPORTED. If any
subsystem is not configured, the value for its key is nil. This might be the case, for
example, for a packaged Smalltalk system that has no file subsystem loaded. The
message setSubsystemType:to: can be used to set or change the system configuration.
The first parameter is the name of the subsystem, which must be one of the
subsystem key strings listed above. The second parameter is one of the allowed
values for the subsystem, or nil, in which case the image has no configuration for
that subsystem.

It is also possible to use the message subsystemType: to make a specific query


regarding the configuration of a particular subsystem. The parameter is once again
one of the subsystem names, and the return value is one of the allowed values or
nil.

Other accessing messages supported are copyright and vmType. The message
copyright answers the systems copyright message, while vmType answers the
virtual machine type for the system.

As mentioned, EmSystemConfiguration also provides support for exiting and saving


the image. The message exit ends the Smalltalk system. Sending saveImage causes a
snapshot to be taken of the current state of the image and writes it to disk. The
exact mechanisms used, such as dialogs invoked, options supported, and name of
the saved file, are platform-specific. When the Smalltalk system is next invoked,
the most recent image snapshot is restored.

Chapter 3. Common Language Implementation 43


44 IBM Smalltalk: Programmers Reference
Chapter 4. Common Process Model
The Common Process Model (CPM) subsystem provides a process model (shared
memory threads) for IBM Smalltalk. Standard operations include process creation
and destruction, as well as process scheduling operations including suspend, resume,
and change priority. Simple synchronization mechanisms are provided by
semaphores. CPM is essentially an implementation of the process model described
in the Blue Book (where you can find more details). Note that CPM does not
include the Blue Book class SharedQueue.

Please refer to The user interface process model on page 225 (in this book) for a
discussion of the input event processing model on which the Common Widgets
user interface is based.

CPM supports the running of multiple independent processes using four classes
named Process, ProcessorScheduler, Delay, and Semaphore. An instance of Process
represents a sequence of message sends that has an independent thread of
execution. A unique instance of the class ProcessorScheduler, the global variable
Processor, is responsible for scheduling these processes for execution by the virtual
machine. Instances of Semaphore class provide a mechanism for process
synchronization and (indirectly) communication. The class Delay enables processes
to synchronize with the real-time clock and to control their execution with fixed
time delays.

Creating a process
A process is normally created by sending the fork or forkAt: message to a block as
shown in the following example. This process is immediately scheduled for
execution. The fork message schedules the new process at the same priority as the
process running the fork, while forkAt: allows the priority to be explicitly specified.
When the new process runs, it evaluates the block. A reference to the new process
can be retained by assigning it to an instance or temporary variable.
| process |
process := [Transcript show: 'The rain in Spain'; cr] fork.

A process can be created in a suspended state by sending the newProcess message


to a block as shown in the next example. The new process is not scheduled for
execution until it is sent the resume message.
| process |
process := [Transcript show: 'The rain in Spain'; cr] newProcess.
process resume.

Protocol also exists for creating a process and passing its creation block a set of
arguments as shown in the following example. Here a string value is passed to a
one-argument block. The number of arguments passed to the block must match the
number of formal block parameters.
| process |
process := [:value | Transcript show: value; cr]
newProcessWith: #('The rain in Spain').
process resume.

Copyright IBM Corp. 1994, 2000 45


Suspending, resuming, and ending a process
Once a process is created, it can be suspended, resumed, or ended. A process is
temporarily halted by sending it the suspend message. The process can be restarted
later by sending it the resume message. A process is stopped permanently, that is,
placed in a state from which it cannot be resumed, when it is sent the terminate
message.

Setting and modifying process priorities


ProcessorScheduler implements a round-robin with priorities scheduler. In other words,
a running process will hold the CPU until one of the following occurs:
v It performs an operation that causes it to suspend (see Table 7 on page 47 for a
list of the primary operations that cause a process to suspend).
v Another process of higher priority is resumed.
v The process is ended.

At that point, the scheduling algorithm ensures that the highest priority process
that is ready to run next. If there is more than one process satisfying this criterion,
then the process that has been waiting longest is selected for execution.

Consequently, the order in which processes run can be influenced by changing the
priority assigned to a process. There are seven preassigned priority levels. The
priority constants in increasing order are as follows:
1. systemBackgroundPriority
The priority of a system background process
2. userBackgroundPriority
The priority of a user background process
3. userSchedulingPriority
The priority of the user interface process (also the default priority of any
process forked by the user interface process)
4. userInterruptPriority
The priority of any process forked by the user interface that should be run
immediately
5. lowIOPriority
The usual priority for input/output processes
6. highIOPriority
The priority of the process monitoring the local network devices
7. timingPriority
The priority of the process monitoring the real-time clock

On many platforms the priorities are actually mapped to the integers 1 through 7;
however, it is poor programming style to reference the priorities directly as integral
values. The preferred approach is to use the priority accessors provided by
ProcessorScheduler. You can query a process priority and modify its value as
required. In the next example, the priority and priority: messages are used to query
and modify the priority of the process. Note, however, that if a priority is changed,
the change has no effect on scheduling until the next process switch.

46 IBM Smalltalk: Programmers Reference


| process |
process := [Transcript show: 'The rain in Spain'; cr] newProcess.
Transcript show: process priority printString; cr.
process priority: Processor userBackgroundPriority.
Transcript show: process priority printString; cr.

Table 7. Primary operations that cause a process to suspend


Operation Relevant method Methods class
Directly suspend a process suspend Process
Wait on a semaphore wait Semaphore
Wait on a delay wait Delay
Open a debugger on an reportError:resumable:startBP EtWindowSystemStartUp
active process
Debug an active process addProcess EtDebugger
from within a debugger
Use execLongOperation to execLongOperation: EtWindow
evaluate a block in the
background (causes the
regular UI process to
suspend)
Resume another process of resume Process
higher priority
Create another process of forkAt: Block
higher priority (when the
next context switch occurs)
Change the priority of priority: Process
another process to be greater
than this process (when the
next context switch occurrs)

Synchronization using semaphore and delay


Instances of class Semaphore are used for interprocess synchronization. Semaphores
can be thought of as containers for signals. (Signals are not on/off switches;
multiple signals can be queued up.) Sending the message signal to a semaphore
adds a signal, while sending the message wait removes a signal. If a semaphore
that has no signals is sent the wait message, the process running the wait is
suspended until the semaphore is sent a signal message. If more than one process
is waiting on a semaphore when a signal is sent, the longest waiting process is
resumed.

The following example illustrates the use of Semaphores. A process is forked at a


high priority. It blocks, waiting on a Semaphore. Each time the user interface signals
the Semaphore the forked process unblocks, increments a counter, and blocks on the
Semaphore again. The user interface process then prints the counter value. After the
example is run, the Semaphore becomes garbage, and the original process
disappears with the Semaphore.
| count aSemaphore testProcess output |
output := WriteStream on: String new.
count := 1.
aSemaphore := Semaphore new.
testProcess := [
[true] whileTrue: [
aSemaphore wait.
output nextPutAll: 'Process has acquired the Semaphore. ',

Chapter 4. Common Process Model 47


'Incrementing count.'; cr.
count := count + 1.]
] forkAt: Processor timingPriority.

aSemaphore signal.
output nextPutAll: 'After First signal, count = ', count printString; cr.
aSemaphore signal.
output nextPutAll: 'After Second signal, count = ', count printString; cr.
Transcript show: output contents
Transcript Output:

Process has acquired the Semaphore. Incrementing count.


After First signal, count = 2
Process has acquired the Semaphore. Incrementing count.
After Second signal, count = 3

Instances of class Delay are used to effect synchronization with the real-time clock,
and to postpone process execution for a specified time period. Postponement can
be specified in milliseconds, seconds, or until a specific time. Each Delay has a
resumption time specified in milliseconds since midnight. When the Delay is sent the
wait message, the process running the wait operation is suspended until the
real-time clock advances to the resumption time specified by the Delay.

To illustrate the use of Delay, evaluate the following code fragment. A message is
displayed on the Transcript after about 15 seconds.
[(Delay forSeconds: 15) wait.
Transcript show: 'Executing delay example'; cr] fork.

Block evaluation methods


Additional Block methods are supported in CPM that allow a block to be
evaluated, with or without arguments, and the result of the last statement
answered. These methods follow in Block evaluation methods.

Protocol synopsis
The principle classes and methods in CPM are summarized below.

In Block evaluation methods, anObject, anotherObject, and thirdObject refer to the


first, second, and third parameters of the method.

Block evaluation methods: The following instance methods pertain to block


evaluation:
argumentCount
Answers the number of arguments to the receiver.
value Evaluates the receiver. Answers the result of the last statement to be
evaluated.
value: Evaluates the receiver with parameter anObject. Answers the result of the
last statement to be evaluated.
value:value:
Evaluates the receiver with parameters anObject and anotherObject. Answers
the result of the last statement to be evaluated.
value:value:value:
Evaluates the receiver with parameters anObject, anotherObject, and
thirdObject. Answers the result of the last statement to be evaluated.

48 IBM Smalltalk: Programmers Reference


valueWithArguments:
Evaluates the receiver with the specified array. Answers the result of the
last statement to be evaluated.
valueOnReturnDo:
Evaluates the receiver. Answers the result of the last statement to be
evaluated. When the receiver returns, evaluate the specified block.
value:onReturnDo:
Evaluates the receiver with parameter anObject. Answers the result of the
last statement to be evaluated. When the receiver returns, evaluate the
specified block.
value:value:onReturnDo:
Evaluates the receiver with parameters anObject and anotherObject. Answers
the result of the last statement to be evaluated. When the receiver returns,
evaluate the specified block.
value:value:value:onReturnDo:
Evaluates the receiver with parameters anObject, anotherObject, and
thirdObject. Answers the result of the last statement to be evaluated. When
the receiver returns, evaluate the specified block.
valueWithArguments:onReturnDo:
Evaluates the receiver with the specified array. Answers the result of the
last statement to be evaluated. When the receiver returns, evaluate the
specified block.

Tip: These additional Block methods allow a block to be evaluated, with or without
arguments, and the result of the last statement answered. anObject,
anotherObject, and thirdObject parameters refer to the first, second, and third
parameters of the method.

Process-related block methods


The following instance methods pertain to process-related blocks:
fork Creates and schedules a new process to evaluate the receiver block, using
the priority of the activeProcess (Processor activePriority).
forkAt: Creates and schedules a new process to evaluate the receiver block, at the
specified priority.
newProcess
Creates a new process to evaluate the receiver block, using the priority of
the activeProcess, and places it in suspended state. The new process is
scheduled by sending it the resume message.
newProcessWith:
Creates a new process to evaluate the receiver block with the specified
arguments, using the current priority, and places it in suspended state. The
new process is scheduled by sending it the resume message.

Process methods
The following instance methods pertain to processes:
priority
Answers the receivers priority.
priority:
Sets the receivers priority to be the specified value.

Chapter 4. Common Process Model 49


queueInterrupt:
Runs the argument block in the receiving process.
resume Resumes the receiver.
suspend
Places the receiver in suspended state.
terminate
Terminates the receiver.

Porting tip: ProcessorScheduler is called ProcessScheduler in Smalltalk/V.

ProcessorScheduler methods
These messages are sent to the Processor global variable.

The following instance methods pertain to ProcessScheduler:


activePriority
Answers the priority of the current active process in the system.
activeProcess
Answers the current active process in the system.
highIOPriority
Answers the priority of the process monitoring the local network devices.
lowIOPriority
Answers the usual priority for input/output processes.
signal:atTime:
Informs the system that it should signal a semaphore at the resumption
time, specified in milliseconds since midnight.
systemBackgroundPriority
Answers the priority of a system background process.
timingPriority
Answers the priority of the process monitoring the real-time clock.
userBackgroundPriority
Answers the priority of a user background process.
userInterruptPriority
Answers the priority of any process forked by the user interface that
should be run immediately.
userSchedulingPriority
Answers the priority of the user interface process; also the default priority
for any process forked by the user interface process.

Delay class and instance methods


The class methods for Delay include the following:
forMilliseconds:
Answers a new Delay that, when sent the message wait, suspends the
active process for a specified millisecond count.
forSeconds:
Answers a new Delay that, when sent the message wait, suspends the
active process for a specified second count.

50 IBM Smalltalk: Programmers Reference


untilMilliseconds:
Answers a new Delay that, when sent the message wait, suspends the
active process until the millisecond clock reaches the specified value.

The instance methods include the following:


resumptionTime
Answers the millisecond clock value at which to resume the waiting
process.
wait Suspends the active process for the delay period.

Semaphore class and instance methods


The class methods for Semaphore include the following:
forMutualExclusion
Answers a new semaphore that has one signal.
new Answers a new semaphore that has no signals.

The instance methods include the following:


critical:
Evaluates the argument block and guarantees that there will be no context
switch while the block is being evaluated.
signal Adds a signal to the receiver.
wait Suspends the active process until there is a signal available from the
receiver.

Chapter 4. Common Process Model 51


52 IBM Smalltalk: Programmers Reference
Chapter 5. Common File System
The Common File System subsystem (CFS) enables you to access the capabilities of
the platform file system in a platform-independent manner.

Accessing the capabilities of the Common File System


Capabilities of the platform file system include basic file protocols, stream
protocols, portability protocols, and error handling protocols. Classes in the
Common File System are prefixed by the letters Cfs, for example: CfsFileDescriptor.

Basic file protocols


The Common File System provides a set of low-level file protocols based on the
POSIX.1 standard. This includes standard protocols for the following:
v Unbuffered input and output on file descriptors: open, close, read, write, lseek,
rewind, flush, and size.
v Searching directory entries: opendir, closedir, readdir, and rewinddir
v Managing files and directories: chdir, getcwd, remove (unlink), rename, mkdir,
and rmdir
v Testing existence of files and directories, and obtaining file statistics: stat
v File locking and sharing

Stream protocols
The Common File System also extends POSIX.1 to provide file stream protocols
that conform to and extend those specified by the Blue Book. These protocols
provide simplified buffered input and output, and are designed to cooperate with
and complement the basic file protocols. In fact, the stream protocols themselves
are implemented portably by using the base file protocols.

Portability protocols
The Common File System also provides protocols to deal with unavoidable
platform-specific attributes, such as line delimiters, path separators, file system
roots, and startup directory, in a portable manner. Extensions to POSIX.1 are also
provided for the following:
v Copying
v Suppressing platform error dialogs
v Obtaining volume information: caseSensitive, preservesCase, maximumFileLength,
volumeName, and volumeType.

Error handling protocols


All basic Common File System methods and file stream class methods support
integrated error handling by answering an instance of CfsError if an error occurs.
Error constants are the same on all platforms, and are a subset of the POSIX.1 error
constants. To determine if an error has occurred, send isCfsError to the result
returned from the operation. Only instances of CfsError answer true to this
message. All other objects answer false.

Copyright IBM Corp. 1994, 2000 53


CfsConstants pool dictionary
The Common File System subsystem provides a pool dictionary called CfsConstants
that contains constants for specifying values such as file access modes, creation
flags, share modes, and error numbers.

Basic classes
CfsVolumeInfo
The CfsVolumeInfo class enables you to obtain information about a volume.

CfsDirectoryDescriptor
The CfsDirectoryDescriptor class provides services for creating, removing, and
querying directories.

CfsDirectoryEntry
The CfsDirectoryEntry class represents the information stored in the directory entry
for a particular file.

CfsFileDescriptor
The CfsFileDescriptor class provides services for creating, removing, querying,
reading, and writing files. This includes position control and locking services.

CfsStat
The CfsStat class provides a mechanism for obtaining statistical information (file
size, file type, modification date) about files and directories.

CfsError
The CfsError class provides a mechanism for obtaining error information pertaining
to file stream creation, file, directory, and file stat operations.

Specifying file names and paths


File names, directories, and paths are specified using strings. All Common File
System methods accept and return file names and paths using the syntax of the
platform file system. You can use both absolute paths and paths relative to the
current working directory.

Portable file names


Portable file names should consist of no more than eight characters, plus an
optional extension consisting of a period followed by no more than three
characters. Valid characters are uppercase and lowercase letters, digits, the
underscore (_), and the hyphen (-). For portability, applications should not assume
that file names are case sensitive or case insensitive.

The CfsVolumeInfo class can be used to portably determine file name length and
case sensitivity for a specific volume. See Obtaining volume information on
page 57.

All CFS protocols support the use of DBStrings as file names in locales that
support the use of double-byte characters (characters whose values range from 0 to
65535). The limitations on file name length imposed by many platforms are often
determined by the number of bytes in the file name rather than the number of
characters. Because each character in a DBString can require either one or two
bytes of storage, a DBStringcontaining eight characters is not necessarily a valid file
name in any given operating system. The use of file names containing double-byte
characters is not portable.

54 IBM Smalltalk: Programmers Reference


File system roots
Because some platforms have multiple file system roots while others do not, it is
important to be able to obtain the root(s) of the file system. Sending rootDirectories
to the CfsDirectoryDescriptor class answers an array of one or more fully qualified
paths to the root directories of the file system. Examples are shown below:
CfsDirectoryDescriptor rootDirectories.
"For OS/2: #('A:\' 'B:\' 'C:\' 'D:\' 'F:\' 'G:\')"
"For UNIX: #('/')"

Path separators
The path separator used for specifying file names is dependent on the underlying
file system. Applications can obtain the path separator by sending either the
pathSeparator or pathSeparatorString messages to CfsDirectoryDescriptor class, which
answers the path separator as a character, and as a string, respectively. Both have
their uses, as demonstrated here:
"Check if a path ends with a path separator"
|'/usr/local/bin/' last = CfsDirectoryDescriptor pathSeparator.

"Build a path starting with the first root directory"


| firstRoot |
firstRoot := CfsDirectoryDescriptor rootDirectories first.
|firstRoot, 'data', CfsDirectoryDescriptor pathSeparatorString, 'mystuff'.

Managing files and directories


The CfsFileDescriptor and CfsDirectoryDescriptor classes provide common protocol
for file and directory management operations. These protocols are Smalltalk
equivalents of POSIX.1 functions.

Current working directory


A string specifying the full path for the current working directory can be obtained
by sending getcwd to the CfsDirectoryDescriptor class. To change the current
working directory, use chdir: as in the following examples:
CfsDirectoryDescriptor chdir: 'C:\WINDOWS'.
CfsDirectoryDescriptor chdir: 'system'.
CfsDirectoryDescriptor chdir: CfsDirectoryDescriptor rootDirectories last.

Creating and removing directories


A new directory can be created by sending the mkdir: message to the
CfsDirectoryDescriptor class. An existing directory can be removed by sending the
rmdir: message to the CfsDirectoryDescriptor class.
CfsDirectoryDescriptor mkdir: 'data'.

CfsDirectoryDescriptor rmdir: 'data'.

Note: Deleting the current working directory can result in unrecoverable errors
due to platform operating system behavior. Platforms with drives demand
special caution, because they usually maintain a separate working directory
for each drive. Changing the current working directory to another drive
before deleting the old working directory will not necessarily prevent an
error from occurring, because the current working directory associated with
the original drive can remain unchanged. Instead, change the current
working directory to the root of the drive containing the directory you want
to delete.

Chapter 5. Common File System 55


"The wrong way"
"Current working directory might be C:\USELESS"
CfsDirectoryDescriptor chdir: 'D:\APPLICATION'.
CfsDirectoryDescriptor rmdir: 'C:\USELESS'.

"The right way"


"Current working directory might be C:\USELESS"
"Prevents working dir for drive C: from being USELESS"
CfsDirectoryDescriptor chdir: 'C:\'.
"If you really want to end up in this dir"
CfsDirectoryDescriptor chdir: 'D:\APPLICATION'.
CfsDirectoryDescriptor rmdir: 'C:\USELESS'.
CfsDirectoryDescriptor chdir: 'C:\'.

Deleting files
Delete unwanted files by sending the remove: message to the CfsFileDescriptor class,
as in the following example:
CfsFileDescriptor remove: 'unwanted.fil'.

Renaming files
Rename a file by sending the rename:new: message to the CfsFileDescriptor class, as
in the following example:
CfsFileDescriptor rename: 'oldname.txt' new: 'newname.txt'.

Note: If supported by the platform file system, rename:new: can also be used to
rename directories or move files between directories on the same drive or
device.

Copying files
A file can be copied by sending the copy:new: message to the CfsFileDescriptor class.
An example is shown below.
CfsFileDescriptor copy: 'filename.txt' new: 'filename.bak'.

Tip: On platforms which do not provide an OS file copy function, copy:new: is


performed using buffered read/write.

Copy can be performed across devices. An example of an efficient move function


implemented using rename and copy is shown below. For an explanation of errno
codes, see Handling errors on page 71.
| result pathA pathB |
"Attempt to move the from pathA to pathB using rename.
If that fails because it cannot rename across devices,
copy it and delete the original."
pathA := 'C:\temp\test.txt'.
pathB := 'A:\test.new'.
result := CfsFileDescriptor rename: pathA new: pathB.
(result isCfsError and: [result errno = EXDEV] )
ifTrue: [
"Platform cannot rename across devices"
result := CfsFileDescriptor copy: pathA new: pathB.
result isCfsError
ifFalse: [
"Copy successful, delete original"
result := CfsFileDescriptor remove: pathA.
].
].
|result

56 IBM Smalltalk: Programmers Reference


Startup directory
The path to the directory in which the Smalltalk image was started can be obtained
by sending the startUpDirectory message to the CfsDirectoryDescriptor class. This
path is in a format acceptable to CfsDirectoryDescriptor class>>#chdir:, and therefore
might or might not end with a path separator. CfsDirectoryDescriptor
class>>#startUpDirectoryPath answers the full path ending with a path separator.
This latter format is suitable for concatenation of paths.

Obtaining volume information


A CfsVolumeInfo object can be obtained for any directory, and reports file system
characteristics for the volume associated with that directory. Volume information
operations are extensions of the POSIX.1 standard.

CfsVolumeInfo class>>#volumeInfo: answers a new CfsVolumeInfo instance for the


volume associated with the directory specified by the path argument.
CfsVolumeInfo>>#volumeInfo: is functionally identical, however it reuses the receiver
CfsVolumeInfo instance.

CfsVolumeInfo instance protocol is described in this section.

Tip: Wherever possible, the operating system is only queried if and when a
message is sent, and the answered value is cached for later queries to the
same instance.

Volume name and type


The path to the root of a volume can be obtained by sending #volumeName to an
instance of CfsVolumeInfo. The volumeType method answers a string that specifies
the volume type, for example, FAT, HFS, HPFS, MFS, UNIX, or VFAT. The
following example queries the volume type for the default volume:
"Answer the volumeType of the default volume."
(CfsVolumeInfo volumeInfo: CfsDirectoryDescriptor getcwd) volumeType.

File names and directory path case


Case sensitivity of file names and paths on a volume can be determined by
sending caseSensitive to a CfsVolumeInfo instance. The preservesCase method can be
used to determine if the volume preserves the case of file names and paths.

File name length


The maximumFilenameLength method answers the maximum length, in bytes, which
the volume allows for file names and path components. Examples are as follows:
"Answer the maximumFilenameLength for the default volume,
or a CfsError if it cannot be obtained."
| volInfo |
volInfo := CfsVolumeInfo volumeInfo: CfsDirectoryDescriptor getcwd.
|volInfo isCfsError
ifTrue: [volInfo]
ifFalse: [volInfo maximumFilenameLength].

"Answer the maximumFilenameLength, ignoring any error."


| volInfo |
(volInfo := CfsVolumeInfo new) volumeInfo: CfsDirectoryDescriptor getcwd.
|volInfo maximumFilenameLength.

Chapter 5. Common File System 57


Volumes with different file name lengths
The formatFilename: method can be used to shorten its filename argument to fit the
volumes maximumFilenameLength restrictions by dropping vowels and truncating,
if necessary. This method assumes that a maximumFilenameLength of 12 indicates
that file names should consist of eight characters, optionally followed by a period
and a three character extension. The formatFilename: method does not detect or
suppress characters that are not valid.

File names should be composed of the POSIX.1 portable file name characters,
which are listed in Portable file names on page 54.

Note: If volume information cannot be obtained due to an OS error, caseSensitive,


preservesCase and maximumFilenameLength will be set to conservative values
(true, false, and 12 respectively) and formatFilename: will operate on these
values. This behavior is provided for cases where the volume information
cannot be obtained due to an OS error, such as when no disk is present in
drive.

The following example forces a file name to conform to the file name length and
case of the volume specified by path. For further information on the
systemErrorDialog: method, see Suppressing system error dialogs on page 73.
"Modify the file name for the file about to be saved to path,
ignoring all errors, including drive not ready."
| filename path state volumeInfo newFilename |

filename := 'aVeryLongFilenameWhichWontFitOnSomeSystems.Text'.
path := 'a:\workdir'.

"Obtain volume information"


(volumeInfo := CfsVolumeInfo new) volumeInfo: path.
"Force the filename to fit"
newFilename := volumeInfo formatFilename: filename.
"Answer the full path"
|path last = CfsDirectoryDescriptor pathSeparator
ifTrue: [ path, newFilename]
ifFalse: [ path, CfsDirectoryDescriptor pathSeparatorString, newFilename]

Searching directories
The CfsDirectoryDescriptor class provides common protocols for reading the
contents of file system directories. It also supports pattern matching by name and
type.

Opening a directory for searching


Open a specific directory for searching by sending the opendir:pattern:mode: message
to the CfsDirectoryDescriptor class. This answers a CfsDirectoryDescriptor instance
that is similar to the CfsFileDescriptor instance used to access regular files.

The opendir:pattern:mode: message takes three arguments. The first argument is a


string specifying the path of the directory to be searched.

The second argument is a pattern for matching entry names. The $* wildcard
character matches 0 or more characters, the $? wildcard character matches any
single character. A pattern of * always matches all files, regardless of platform.

The third argument specifies the type of files to match. It is a logical inclusive-OR
of one or more of the following constants, which are defined in the CfsConstants

58 IBM Smalltalk: Programmers Reference


pool dictionary:
Table 8. Directory search modes
Pool variable Description
FREG If specified, matches regular files.
FDIR If specified, matches directories.
FSPECIAL If specified, matches files that are neither regular files nor directories.

The following are some examples of opening directory streams:


| dd |
"Get search handle for files of all types in the current working directory"
(dd := CfsDirectoryDescriptor
opendir: '.'
pattern: '*'
mode: FREG | FDIR | FSPECIAL) isCfsError
ifTrue: [|self error: dd message].
dd closedir.
| dd |
"Get search handle for all regular files ending in '.bat'"
(dd := CfsDirectoryDescriptor
opendir: 'c:\'
pattern: '*.bat'
mode: FREG) isCfsError
ifTrue: [|self error: dd message].
dd closedir.
| dd |
"Get search handle for all special files in the root of drive C:"
(dd := CfsDirectoryDescriptor
opendir: 'c:\'
pattern: '*'
mode: FSPECIAL) isCfsError
ifTrue: [|self error: dd message].
dd closedir.

Pattern matching uses platform-specific optimizations on platforms with direct


support for pattern-matched searches.

Reading directory entries


There are three means of reading directory entries, each intended for a particular
purpose.

Using readdir
Sending the readdir message to a CfsDirectoryDescriptor instance causes the instance
to answer a new CfsDirectoryEntry instance representing the next directory entry
matching the specified pattern and type. A CfsDirectoryEntry instance understands
the same messages as a CfsStat instance, plus the dName message, which answers
the name of the file represented by the directory entry as a string. See Testing
existence and obtaining other file properties on page 74 for a description of the
messages understood by a CfsStat instance.

When there are no more directory entries to be read, the readdir message answers
nil. An example of the use of readdir follows:
"Answer a collection containing directory entries for all regular files
and directories in 'c:\'"
| collection dd de |

collection := OrderedCollection new.

Chapter 5. Common File System 59


(dd := CfsDirectoryDescriptor
opendir: 'c:\'
pattern: '*' mode: FREG | FDIR) isCfsError
ifTrue: [|self error: dd message].

[(de := dd readdir) notNil]


whileTrue: [collection add: de].
dd closedir.
|collection.

Using readdir:
The readdir: message is identical in behavior to readdir, except that instead of
creating a new CfsDirectoryEntry instance, it stores the directory information in the
CfsDirectoryEntry instance supplied as an argument. This technique provides better
performance than creating and discarding a directory entry for each item in the
directory. An example follows:
"Print all regular files and directories in the current working
directory to the transcript"
| dd de |

"Because directory entry data can be discarded after being printed,


there is no point in creating a separate directory entry for each item
read. Instead reuse the same directory entry instance."
de := CfsDirectoryEntry new.
(dd := CfsDirectoryDescriptor
opendir: CfsDirectoryDescriptor getcwd
pattern: '*'
mode: FREG | FDIR) isCfsError
ifTrue: [|self error: dd message].
[(dd readdir: de) notNil]
whileTrue: [Transcript cr; show: de printString].
dd closedir.

Using readdirName
Sending the readdirName message to a CfsDirectoryDescriptor instance answers a
string that is the name of the next directory entry matching the specified pattern
and type. If there are no more entries, it answers nil.

Tip: Do not use readdirName followed by CfsStat>>stat: to get file information,


because this is never faster than using readdir or readdir:, and forces some
platforms to search the directory twice. Using readdir: is the most efficient
technique if more than the file names are needed.

If only the file names are required, using readdirName is more efficient than other
techniques. Two examples are shown below.
"Print the names of all files in the current working directory to the
Transcript"
| dd name |

(dd := CfsDirectoryDescriptor
opendir: '.'
pattern: '*'
mode: FREG | FDIR | FSPECIAL) isCfsError
ifTrue: [|self error: dd message].
[(name := dd readdirName) isNil]
whileFalse: [Transcript cr; show: name].
dd closedir.
"Count the number of files ending in '.new' in the subdirectory called
'work' that is located under the current working directory and
answer the result"
| dd count |
(dd := CfsDirectoryDescriptor

60 IBM Smalltalk: Programmers Reference


opendir: 'work'
pattern: '*.new'
mode: FREG | FDIR | FSPECIAL) isCfsError
ifTrue: [|self error: dd message].
count := 0.
[dd readdirName isNil]
whileFalse: [count := count + 1].
dd closedir.
|count

Tip: For maximum efficiency when using readdirName, ensure that the mode
argument is the inclusive-OR of FREG, FDIR and FSPECIAL. Specifying that
all file types are to be matched eliminates an operating system call to
determine the file type, which would otherwise be needed on some platforms.

Closing the directory descriptor


After all the desired directory entries have been read, the directory descriptor must
be closed by sending it the closedir message before it is discarded. This deallocates
any operating system resources associated with the directory descriptor.

Tip: Sending rewinddir to a CfsDirectoryDescriptor instance has the same effect as


closing and reopening the directory descriptor, but is more efficient on some
platforms. The search will resume from the first entry in the directory.

Using file streams


The Common File System provides buffered I/O services by means of the Blue
Book compliant stream protocols. File streams are the preferred method of
performing file I/O operations within Smalltalk. A complete set of low-level I/O
operations are also provided; see Using low-level file operations on page 64.

File stream classes


The Common File System stream classes consist of three concrete classes that
correspond to the three file access modes, and one abstract class.

CfsReadFileStream
This file stream class provides input services only and corresponds to the POSIX.1
ORDONLY access mode.

CfsWriteFileStream
This file stream class provides output services only and corresponds to the POSIX.1
OWRONLY access mode.

CfsReadWriteFileStream
This file stream class provides both input and output services, and corresponds to
the POSIX.1 ORDWR access mode.

CfsFileStream
This is an abstract class used to create streams on existing open file descriptors,
and to enable you to specify POSIX.1 open modes and flags directly when creating
a new file stream. These operations are descripted in detail in Mixing streams and
file descriptors on page 67.

Opening and closing file streams


The open: and openEmpty: messages are the simplest means of opening a new file
stream instance on a particular file. Both messages are sent to the file stream class

Chapter 5. Common File System 61


representing the desired access mode. The open: message opens an existing file,
while the openEmpty: message truncates an existing file (to size 0) or creates the file
if it does not exist. Accordingly, the CfsReadFileStream class does not respond to the
openEmpty: message.

Generally, open: is used when reading files and openEmpty: is used when writing
new files or overwriting existing files. Some examples follow.
| file |
"Opens an existing file for reading and writing, creates the file if it
does not exist."
(file := CfsReadWriteFileStream open: 'existing.txt') isCfsError
ifTrue: [|self error: file message].
"...Use the file stream for reading and/or writing..."
file close.
| file |
"Opens an existing file for reading only, fails if it does not exist"
(file := CfsReadFileStream open: 'existing.txt') isCfsError
ifTrue: [|self error: file message].
"...Use the file stream for reading..."
file close. "When done, close the file stream, which closes the file."
| file |
"Opens a new file for writing only, truncates it to size 0 if it already exists."
(file := CfsWriteFileStream openEmpty: 'new.txt') isCfsError
ifTrue: [|self error: file message].
"...Use the file stream for writing..."
file close.

Once all desired operations have been performed, the file stream instance must be
closed by sending it the close message before it is discarded. This closes the file, by
deallocating any operating system resources associated with the file stream, and
flushing any cached data to disk.

On double-byte platforms, the platform character encoding does not necessarily


match the character encoding used within Smalltalk. As a result, Smalltalk strings
must be converted to and from the platform representation as they are written to
and read from files. When the Smalltalk and platform encodings differ, the stream
answered by the open: and openEmpty: messages will not be an instance of the class
to which the message was sent. In such cases the open: and openEmpty: messages
answer a specialized stream that conforms to the requested protocols and manages
the conversion of Smalltalk strings to the appropriate platform representation.

In these cases, it is important to use the isCfsError message to test the result of the
open: and openEmpty: operations rather than testing the class of the returned object
(for example, returnedObject class == CfsReadFileStream).

Reading and writing file streams


File streams have the same protocol as CLDT Streams. For the complete set of
methods supported by the Stream class, see Stream classes on page 22. The
following example uses a CfsReadFileStream to read data from an existing file, and a
CfsWriteFileStream to write the data to a newly created file.
| oldData newData |
(oldData := CfsReadFileStream open: 'oldData.txt') isCfsError
ifTrue: [|self error: oldData message].
(newData := CfsWriteFileStream openEmpty: 'newData.txt') isCfsError
ifTrue: [|self error: newData message].
newData nextPutAll: oldData contents.
oldData close.
newData close.

62 IBM Smalltalk: Programmers Reference


Note: The indexing methods for file streams answer and accept the byte position
in the file, not the character position. In contrast, the indexing operations of
CLDT Streams answer and accept character positions in the buffer being
streamed over. When file streams are used with single-byte characters
exclusively, the character position and byte positions are the same. However,
this is not the case when a mixture of single and double-byte characters are
used.

When used with file streams, the copyFrom:to:, position, position:, size, and
skip: messages operate based upon byte indexes and sizes, not character and
sizes. The other file stream operations operate in the same manner as the
CLDT Stream protocols. For portability, use the next and next: protocols for
stream positioning.

Characters versus bytes


All file stream operations accept both character (Character, DBString, String) and
byte (Integer, ByteArray) arguments interchangeably as appropriate for the platform.
For example, nextPut: accepts either a character or an integer between 0 and 255 on
platforms that use single-byte characters, and a character or an integer between 0
to 65535 on platforms that use double-byte characters. The nextPutAll: message
accepts an instance of String, DBString, or ByteArray as appropriate for the
platform.

Additionally, you can change the type of objects answered by file stream
operations such as contents, ext, nextLine, upTo:, and upToAll: by using the isBytes:
and isCharacters: messages to specify the type of data that is being streamed over,
as shown in the following example:
"Open a text file for reading"
| file contents |
file := CfsReadFileStream open: 'readme.txt'.
file isCharacters: true.
"We are streaming over characters, answer file contents as characters"
contents := file contents.
file close.
|contents
"Open a bitmap image file for reading"
| file contents |
file := CfsReadFileStream open: 'looknice.bmp'.
file isBytes: true.
"We are streaming over bytes, answer file contents as bytes"
contents := file contents.
file close.
|contents

The data type being used by a stream can be determined using the Boolean
messages isBytes and isCharacters. These messages are opposites; exactly one
returns true at any given time.

Tip: Care must be taken when using Stream methods position, position:, size, and
copyFrom:to: with CfsFileStreams on a double-byte locale, because these are
answered in bytes and not in characters.

Line delimiters
By default, a stream uses the platform file systems line delimiter for operations
such as cr and nextLine. A string representing the current line delimiter can be
obtained by sending the lineDelimiter message to a file stream instance, and can be
changed to an arbitrary string by sending the lineDelimiter: message. This makes it

Chapter 5. Common File System 63


possible to adopt a single platforms file convention as the standard for all
platforms, or to use nextLine to read files written on other platforms. The following
table lists the line delimiter constants defined in the CldtConstants pool dictionary:
Table 9. Line Delimiter constants in the CldtConstants pool dictionary
Pool variable Description
LineDelimiter The default platform line delimiter
PMLineDelimiter The line delimiter used by OS/2 Presentation Manager
| UNIXLineDelimiter The line delimiter used by AIX and OS/390
WINLineDelimiter The line delimiter used by MS-DOS/Windows

The following example demonstrates the use of the lineDelimiter: message and line
delimiter constants, as well as their effect on the cr message:
| file |
file := CfsReadWriteFileStream openEmpty: 'testing.txt'.

"Use OS/2 line delimiter"


file lineDelimiter: PMLineDelimiter.
file cr; nextPutAll: '<-os/2 line delimiter'.
"Set back to default line delimiter"
file lineDelimiter: LineDelimiter.
file cr; nextPutAll: '<-default line delimiter'.
"Use an arbitrary line delimiter"
file lineDelimiter: '[end of line]'.
file cr; nextPutAll: '<-arbitrary line delimiter'.
file close.

Tip: Use upToAll: and skipToAll: to scan up to various special character sequences.
This is better than changing the line delimiter to the special sequence with
lineDelimiter: and then using nextLine.

Using low-level file operations


The CfsFileDescriptor class provides a common protocol for low-level file
operations. These protocols are Smalltalk equivalents of the POSIX.1 file descriptor
functions.

Opening files
A file is opened by sending the open:oflag: message to the CfsFileDescriptor class,
which answers a new CfsFileDescriptor instance to act as the receiver for I/O
operations to that file. The first argument is a String or DBString specifying the
path of the file to be opened; the second is an integer specifying the inclusive-OR
of exactly one access mode and zero or more open flags. Access modes and flags
are defined in the CfsConstants pool dictionary.

The following table describes open access modes and flags.


Table 10. Open access modes and flags (OR together)
Open access modes (specify only one)
ORDONLY Open for reading only
OWRONLY Open for writing only
ORDWR Open for reading and writing
Open flags (specify zero or more)

64 IBM Smalltalk: Programmers Reference


Table 10. Open access modes and flags (OR together) (continued)
OAPPEND Causes the file offset to be set to the end of the file prior to EACH
write
OCREAT Causes the file to be created if it does not exist. Has no effect if the file
exists, except as noted under OEXCL.
OEXCL If OCREAT and OEXCL are both specified, fails if the file exists.
OTRUNC If the file exists and is a regular file, and the file is successfully opened
ORDWR or OWRONLY, causes the file to be truncated to zero length
with other platform attributes unchanged.

Some examples of the use of access modes and open flags follow:
| fd |
"Opens an existing file for reading, fails if it does not exist"
(fd := CfsFileDescriptor
open: 'exists.txt'
oflag: ORDONLY) isCfsError
ifTrue: [|self error: fd message].
fd close.
| fd |
"Opens a new file for writing, fails if it exists"
(fd := CfsFileDescriptor
open: 'new.txt'
oflag: OWRONLY | OCREAT | OEXCL) isCfsError
ifTrue: [|self error: fd message].
fd close.
| fd |
"Opens an existing file for reading and writing,
or creates it if it does not exist"
(fd := CfsFileDescriptor
open: 'log.txt'
oflag: ORDWR | OCREAT) isCfsError
ifTrue: [|self error: fd message].
fd close.
| fd |
"Ensures that the opened file is empty: creates if it does not exist,
truncate to size 0 if it does."
(fd := CfsFileDescriptor
open: 'empty.txt'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [|self error: fd message].
fd close.

Closing file descriptors


Once all desired operations have been performed on a file descriptor, it must be
closed before being discarded. This is accomplished by sending the file descriptor
the close message, which deallocates all operating system resources associated with
it.
| fd |
(fd := CfsFileDescriptor
open: 'example.txt'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [|self error: fd message].

"... Put file operations here ..."


fd close.
"Close file when done"

Chapter 5. Common File System 65


Reading and writing data
Data can be transferred between a file and a buffer by sending the
read:startingAt:nbyte: and write:startingAt:nbyte: messages to the CfsFileDescriptor
instance returned by the open:oflag: message. The first argument specifies the buffer,
which can be either a String or ByteArray instance. The second argument specifies
the position in the buffer to or from which data is to be transferred. The third
argument specifies the number of bytes to be transferred. The messages answer the
number of bytes that were successfully read or written.

Note: The startingAt parameter refers to the position in the buffer, not in the file.
To change the position in the file (the file offset) use the lseek:whence: message.
"Example of read/write: low level file copy using buffers"
| source target buf bytesRead bufSize |

(source := CfsFileDescriptor
open: 'example.txt'
oflag: ORDONLY) isCfsError
ifTrue: [|self error: source message].
(target := CfsFileDescriptor
open: 'example.bak'
oflag: OWRONLY | OCREAT | OTRUNC) isCfsError
ifTrue: [|self error: target message].
"Choose a reasonable size"
bufSize := 4096.
"Could also use a ByteArray"
buf := String new: bufSize.
[(bytesRead := source read: buf startingAt: 1 nbyte: bufSize) > 0]
whileTrue: [
bytesRead > (target write: buf startingAt: 1 nbyte: bytesRead)
ifTrue: [
"Fewer bytes written than requested - might indicate full
file system"
source close.
target close.
|self error: 'Unable to copy file. File system could be full.']].
source close.
target close.

Tip: There is no need to implement a low-level file copy function. CfsFileDescriptor


CfsFileDescriptorclass>>#copy:new: portably provides this functionality using
the most efficient means for each platform.

Changing the file offset


The lseek:whence: message is used to query and set the file offset. This is the
position in the file used by read:startingAt:nbyte: and write:startingAt:nbyte: when
transferring data. A successful read or write operation automatically sets the file
offset to point after the last byte read or written.

The lseek:whence: message is sent to an open CfsFileDescriptor instance. The first


argument, offset, is an integer offset into the file. The second argument, whence, is
one of the constants shown in the following table, which are defined in the
CfsConstants pool dictionary. The lseek:whence: message answers an integer
representing the new file offset.

66 IBM Smalltalk: Programmers Reference


Table 11. Whence constants for the lseek:whence: message
Pool variable Description
SEEKSET The file offset is set to the position specified by the first argument. An
offset of 0 sets the file offset to the beginning of the file.
SEEKCUR The file offset is set to its current position plus the number of bytes
specified by the first argument.
SEEKEND The file offset is set to the file size plus the number of bytes specified
by the first argument.

Some examples of the use of lseek:whence: follow:


| fd current |

"Open an existing file for reading, fail if it does not exist"


(fd := CfsFileDescriptor
open: 'exists.txt'
oflag: ORDONLY) isCfsError
ifTrue: [|self error: fd message].
"Get the current file pointer position"
current := fd lseek: 0 whence: SEEKCUR.
Transcript cr; show: 'Current position: ', current printString.

"Seek to 5 bytes before the end of the file"


fd lseek: -5 whence: SEEKEND.
"Seek to the beginning of the file"
fd lseek: 0 whence: SEEKSET.

"Seek after 10th byte of file, next read or write


will start with 11th byte"
fd lseek: 10 whence: SEEKSET.

"Rewind file pointer by 20 bytes"


fd lseek: -20 whence: SEEKCUR.
fd close.

Other low-level operations


The following list describes what happens when the specified methods message is
sent to an open CfsFileDescriptor instance:
rewind Rewinds the file pointer to the beginning of the file.
size Answers the size of the file in bytes without affecting the position of the
file pointer.
flush Forces all changes to the file to be written to disk.

Mixing streams and file descriptors


In some applications, it might be necessary to mix the convenience of streams with
the low-level capabilities of file descriptors.

Using access modes and flags with file streams


It is possible to open a file stream on an existing file descriptor by sending the on:
message to the CfsFileStream class. The on: message answers an instance of the
concrete stream class corresponding to the access mode of the file descriptor. An
example of converting a file descriptor into a file stream is shown below:

Chapter 5. Common File System 67


| fd file |
fd := CfsFileDescriptor
open: 'new.fil'
oflag: ORDWR | OCREAT | OEXCL.
fd isCfsError
ifTrue: [|self error: fd printString].
file := CfsFileStream on: fd.
"Close the file stream - this will automatically close the file
descriptor as well"
file close.

The on: message is also useful if the file is to be opened with a special share mode,
using the open:oflag:share: message, which was discussed in File locking on page
68. An example follows:
| file fd |
(fd := CfsFileDescriptor
open: 'a.fil'
oflag: ORDWR
share: ODENYRDWR) isCfsError
ifTrue: [|self error: fd message].
file := CfsFileStream on: fd.
file close.

Performing low-level file operations on streams


Because the file streams in IBM Smalltalk are implemented using the
CfsFileDescriptor class, it is possible to obtain the CfsFileDescriptor instance that a
file stream is streaming over. To do this, send the fileDescriptor message to the file
stream instance. The CfsFileDescriptor instance that is answered can then be used
for low-level file descriptor operations. An example of cooperation between
streams and file descriptors is shown below:
"An example of combining file descriptor locking with file streams"
| fileStream |

(fileStream := CfsReadWriteFileStream openEmpty: 'lockable.fil') isCfsError


ifTrue: [|self error: fileStream message].

fileStream nextPutAll: 'This is a LOCKED area'.


"Lock the word LOCKED"
fileStream fileDescriptor lock: FMDLOCK start: 10 len: 6.

"Unlock it"
fileStream fileDescriptor unlock: FMDLOCK start: 10 len: 6.

"Close the file stream"


fileStream close.

File locking and share modes


The Common File System provides common protocols for file locking and share
modes. However, file locking and share modes are not uniformly supported by all
platform file systems.

File locking
There are two general locking schemes, advisory locking and mandatory locking.
Advisory locks have no effect on clients that do not explicitly check for a lock.
Mandatory locks do not require clients to explicitly check for locks, but represent a
security risk because it is possible to interfere with the normal operation of a
system by placing mandatory locks on essential files. For this reason mandatory

68 IBM Smalltalk: Programmers Reference


locks are excluded from the POSIX.1 standard. A platform file system usually
supports either advisory or mandatory locking but not both.

The locking constants in the CfsConstants pool dictionary appear in the following
table:
Table 12. File locking constants
Pool variable Description
FRDLOCK Specifies a shared (read) advisory lock. A shared advisory lock prevents
any other client from setting an exclusive advisory lock on any portion
of the protected area. Noncooperating clients can read or write in
protected areas.
FWRLOCK Specifies an exclusive (write) advisory lock. An exclusive advisory lock
prevents any other client from setting a shared or exclusive advisory
lock on any portion of the protected area. Noncooperating clients can
read or write in protected areas.
FMDLOCK Specifies an exclusive mandatory lock. An exclusive mandatory lock
prevents any other client from reading, writing, or locking any portion
of the protected area.

Determining supported lock types


In general, a platform operating system supports either advisory locking or
mandatory locking, but not both. To determine whether a lock type is supported,
send the supportsLockType: message to the CfsFileDescriptor class, with the desired
locking constant as an argument. If the specified type of lock is supported, true is
answered. Otherwise, false is answered. An example follows:
"Need exclusive lock, preferably mandatory. Answer what is available"
(CfsFileDescriptor supportsLockType: FMDLOCK)
ifTrue: [|FMDLOCK]
ifFalse: [
(CfsFileDescriptor supportsLockType: FWRLOCK)
ifTrue: [|FWRLOCK]
ifFalse: [|self error: 'No exclusive lock types are supported']].

Tip: Locking might have no effect unless the file is being accessed by what the
operating system considers to be distinct processes. With some network
software, locking can only be effective if the file is being accessed from
different machines. These are limitations in the services provided by the
operating system or network.

Locking and unlocking regions


Sending the lock:start:len: message to a CfsFileDescriptor instance sets a segment lock
on the file associated with the CfsFileDescriptor instance. The first argument is one
of the file locking constants described in the table in the preceding section. The
second argument is an integer specifying the zero-based offset of the first byte to
be locked. The third argument is an integer specifying the number of bytes to be
locked. If the specified locking constant is not supported, a CfsError instance is
answered. To release a lock, use the unlock:start:len: message. The arguments are
identical. Examples are as follows:
| fd theSize |
(fd := CfsFileDescriptor
open: 'lockable.fil'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [|self error: fd message].
fd write: 'This is a TEST LOCK area' startingAt: 1 nbyte: 24.

Chapter 5. Common File System 69


"Lock the word test with a mandatory lock - should really determine appropriate
lock type first as shown in previous section on determining lock type"
fd lock: FMDLOCK start: 10 len: 4.
"Release the lock, must use same lock constant"
fd unlock: FMDLOCK start: 10 len: 4
"Lock the entire file and then unlock it"
"The size is saved so that the region is locked as was unlocked, even
if the file size increased between the operations."
fd lock: FMDLOCK start: 0 len: (theSize :=fd size).
fd unlock: FMDLOCK start: 0 len: theSize.
fd close.

When releasing a lock, the value of the lock type, start, and length arguments must
be exactly the same as those used to set the lock. Otherwise the unlock operation
can fail or have unpredictable behavior. Using the size message when releasing a
lock as shown in the previous example is dangerous, because the size might have
changed since the lock was set. Instead, save the parameters used when locking
and reuse these values when releasing the lock. An example follows:
| fd len |
(fd := CfsFileDescriptor
open: 'lockable.fil'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [|self error: fd message].
fd write: 'This is a TEST LOCK area' startingAt: 1 nbyte: 24.
fd lock: FMDLOCK start: 0 len: (len := fd size).
fd unlock: FMDLOCK start: 0 len: len.
fd close.

The effect of locking overlapping regions or releasing locks that have not been set
is platform-specific. It is recommended that all locks be explicitly released before
closing the file.

Share modes
The Common File System supports four share modes for opening files. The
constants used to specify these modes appear in the following table. These
constants are defined in the CfsConstants pool dictionary.
Table 13. Share mode constants
Pool variable Description
ODENYNONE Other processes can open the file for any access: read-only, write-only,
or read-write.
ODENYRD Other processes can open the file for write-only access, but they cannot
open it for read-only or read-write access.
ODENYWR Other processes can open the file for read-only access, but they cannot
open it for write-only or read-write access.
ODENYRDWR The current process has exclusive access to the file. Other processes
cannot open the file. It is unspecified whether the file can be opened by
the current process.

Selecting valid share modes


Like file locking types, share modes are not uniformly supported by all platforms.
Some platforms can have default share modes that are associated with access
modes, and cannot be changed. For example, opening a file with ORDONLY access
might have the same effect as specifying the ODENYWR share mode, and opening

70 IBM Smalltalk: Programmers Reference


a file with OWRONLY access might have the same effect as specifying
ODENYRDWR. On platforms that do not support specifiable share modes, share
modes are ignored.

To determine whether a particular share mode is supported, send the


supportsShareMode: message to the CfsFileDescriptor class, with the desired share
mode constant as an argument. If the specified share mode is supported, true is
answered. Otherwise, false is answered. The following code fragment determines if
the ODENYRDWR share mode is supported:
"Is ODENYRDWR supported?"
|CfsFileDescriptor supportsShareMode: ODENYRDWR.

Tip: Like locking, share modes might have no effect unless the file is being
accessed by what the operating system considers to be a distinct process. With
some network software, it can only be effective if the file is being accessed
from a different machine.

Opening files using share modes


To open a file descriptor with a specific share mode, send the open:oflag:share:
message to the CfsFileDescriptor class instead of sending open:oflag:. The third
argument specifies the share mode to use. If share modes are not supported, it has
no effect. Here are some examples:
"Open a file read/write with exclusive access"
| fd |
fd := CfsFileDescriptor
open: 'exclusiv.fil'
oflag: ORDWR | OCREAT
share: ODENYRDWR.
fd isCfsError
ifTrue: [|self error: fd printString].
fd close.
"Open a file denying write access to others"
| fd |
fd := CfsFileDescriptor
open: 'readonly.str'
oflag: ORDONLY
share: ODENYWR.
fd isCfsError
ifTrue: [|self error: fd printString].
fd close.

Handling errors
The CfsError class provides a common mechanism for obtaining error information
pertaining to file system operations. If an error prevents the successful completion
of a Common File System operation, the affected method will answer an instance
of CfsError, rather than its normal return value. The CfsError instance contains
information pertaining to the cause of the error.

Errors are detected by sending the isCfsError message to the result returned by any
low-level file system operation, CfsStat operation, or file stream class method such
as open: or openEmpty:. Instances of CfsError are the only objects that answer true to
this message, all other objects answer false.

File stream instance methods in Common File System handle errors in the same
manner as the Stream class in CLDT. If errors are expected when streaming over a
file, set up an exception handler as described in the section on Handling of
exceptions on page 26.

Chapter 5. Common File System 71


Two examples of handling errors follow. The CwMessagePrompter class is described
in Message prompter on page 198.
"Attempt to open a file stream on a file, fail if the file does not exist"
| fileName file text |
file := CfsReadFileStream open: (fileName := 'notthere.txt').
file isCfsError
"Test the file descriptor 'open' result for a CfsError"
ifTrue: [
"Open a dialog displaying the open error"
|CwMessagePrompter new
buttonType: XmOK;
iconType: XmICONERROR;
messageString: ('Unable to open file: %1.%2 Error: %3'
bindWith: fileName
with: LineDelimiter
with: file printString);
prompt].
"If the file does exist, answer the file's contents. Instances of
CfsError are not answered by the file stream instance methods
contents or close"
text := file contents.
file close.
|text
"Attempt to open a file descriptor for a file, fail if the file does not exist"
| fd fileName strings stringBuf result |
fd := CfsFileDescriptor
open: (fileName := 'strings.dat')
oflag: ORDONLY.
fd isCfsError
"Test the file descriptor 'open' result for a CfsError"
ifTrue: [
"Open a dialog displaying the error"
|CwMessagePrompter new
buttonType: XmOK;
iconType: XmICONERROR;
messageString: ('Unable to open file: %1.%2 Error: %3'
bindWith: fileName
with: LineDelimiter
with: fd printString);
prompt].
"If the file exists, read 1000 24-byte strings into an array of strings,
and answer the array. If a file error occurs at any time while reading,
answer the CfsError."
strings := Array new: 1000.
1 to: strings size do: [:i |
stringBuf := String new: 24.
result := fd
read: stringBuf
startingAt: 1
nbyte: stringBuf size.
result isCfsError
"Test the file descriptor 'read' result for a CfsError"
ifTrue: [
fd close.
|result].
strings at: i put: stringBuf.].
fd close.
|strings

The information contained in a CfsError instance is accessed by way of the


following methods:
errno Answers the integer error number for comparison against the error
constants in the CfsConstants pool dictionary.

72 IBM Smalltalk: Programmers Reference


identifier
Answers a string that is the abbreviated error name. This is the same as
the string representing the key in the CfsConstants pool dictionary
associated with the receivers errno value.
message
Answers a short text description of the error.

Printing a CfsError instance using the printString or printOn: message will display
all of the information described above.

The specific errors that can be returned by a method are listed in the method
comment. No other errors are returned. Following is an example of testing for a
specific error that prompts to overwrite existing file:
"Open a new file, prompt for overwrite if it exists"
| fd |

"Open the file, OEXCL causes failure if it exists"


fd := CfsFileDescriptor
open: 'new.fil'
oflag: ORDWR | OCREAT | OEXCL.
fd isCfsError
ifTrue: [
"NOTE: Because an error occurred, fd is not a file descriptor,
it is a CfsError instance."
fd errno = EEXIST
ifTrue: [
"The file already exists"
(CwMessagePrompter new
buttonType: XmYESNO;
iconType: XmICONWARNING;
messageString: 'File exists. Overwrite?';
prompt)
ifTrue: [
"Overwrite the file obtaining a file descriptor"
fd := CfsFileDescriptor
open: 'new.fil'
oflag: ORDWR | OTRUNC.
fd isCfsError
ifTrue: [|self error: fd printString]]
ifFalse: [|self]]
ifFalse: [
"It's some other error - walkback"
|self error: fd printString]].

"Here, fd is guaranteed to be a file descriptor,


do something with the fd, and then close it"
fd close.

When testing for a specific error, compare the CfsError instances errno against the
error constants in the CfsConstants pool dictionary. All application code and
documentation should refer to errors by identifier, for example, ENOENT, rather
than by number, because error constant values can vary from platform to platform.

Suppressing system error dialogs


On some platforms, certain file system errors, such as drive not ready and disk
change required, causes a system error dialog to appear. You can suppress this
dialog by sending systemErrorDialog: to CfsError class. An example is as follows:
"Get volume info for drive A, ignoring drive not ready."
| state volumeInfo |

Chapter 5. Common File System 73


"Turn off system error dialog."
state := CfsError systemErrorDialog: false.
"Get volume info for drive A:"
volumeInfo := CfsVolumeInfo volumeInfo: 'A:\'.
"Reset system error dialog to original state"
CfsError systemErrorDialog: state.
|volumeInfo

Tip: On platforms that do not support dialog suppression, systemErrorDialog: has


no effect.

Testing existence and obtaining other file properties


The CfsStat class provides common protocols for obtaining information about a
particular file from its directory entry. This includes size, type, and modification
date and time.

Obtaining a CfsStat instance


Sending the stat: message to the CfsStat class returns a CfsStat instance containing
the statistics for the file specified as its argument. These statistics can be accessed
by sending messages, that match POSIX.1 stat structure names, to the instance. An
example follows:
"Answer the file size"
|(CfsStat stat: 'bigfile.txt') stSize

Tip: To obtain the exact size of a file, send size to an open file descriptor rather
than using stat:, because CfsFileDescriptor>>#size reflects the true size of the
file based on the end-of-file position as of last write, while CfsStat>>#size
obtains the amount of storage space allocated to the file by reading the files
directory entry. For this reason, the size obtained by sending size to a CfsState
or CfsDirectoryEntry might not match the logical size of the file, and might not
be up-to-date if the file is currently open, even if a flush was just performed
on the file descriptor.

The following messages can be sent to a CfsStat instance. The messages always
answer a meaningful value, because all platforms support the information
associated with them.
isBlk Answers true if the receiver is reporting statistics for a block special file.
Otherwise answers false.
isChr Answers true if the receiver is reporting statistics for a character special
file. Otherwise answers false.
isDir Answers true if the receiver is reporting statistics for a directory. Otherwise
answers false.
isFifo Answers true if the receiver is reporting statistics for a pipe or for a FIFO
special file. Otherwise answers false.
isReg Answers true if the receiver is reporting statistics for a regular file.
Otherwise answers false.
isSpecial
Answers true if the receiver is reporting statistics for a special file.
Otherwise answers false. A special file is any file that is neither a regular
file nor a directory.
stat: Obtains statistics for the specified file and stores them in the receiver.

74 IBM Smalltalk: Programmers Reference


stMode Answers the platform-specific file mode, which indicates the type of file
and other attributes.
stMtime
Answers an array containing the date and time that the file was last
modified.
stSize Answers an integer that is the size in bytes of the regular file. If the
receiver is not reporting statistics for a regular file, the value is unspecified.

The following messages in provide access to additional information that is not


supported uniformly by all platforms. These messages can answer nil if a
particular platform does not support the associated type of information.
stAtime
Answers an array containing the date and time that the file was last
accessed.
stCTime
Answers an array containing the date and time that the files status was
last changed.
stDev Answers the device ID of the device containing the file.
stFtime
Answers an array containing the creation date and time.
stGid Answers the group ID for the file.
stIno Answers the serial number of the file.
stNlink
Answers the number of links for the file.
stUid Answers the user ID for the file.

If repeated stat operations are to be performed, garbage creation can be minimized


by creating an empty CfsStat instance and then reusing it by sending the stat:
message to the instance. An example follows:
| statBuf |
"Create CfsStat instance for buffer"
statBuf := CfsStat new.
"Answer the sum of the sizes of three files"
|(statBuf stat: 'one.fil') stSize + (statBuf stat: 'two.fil')
stSize + (statBuf stat: 'three.fil') stSize

By combining the stat: message with error checking, it is possible to test for file
existence and other special properties. Some examples follow.
"Check file existence: Answer true if path specifies an existing file of any type"
|(CfsStat stat: path) isCfsError not
"Check if a path name is already being used"
"Answer true if path specifies an existing file of any type"
"Customized to consider inaccessible entries to be existing files"
| result |
result := CfsStat stat: path.
result isCfsError
ifTrue: [
result errno = EACCES ifTrue: [|true].
|false]
ifFalse: [|true].

Chapter 5. Common File System 75


"Answer true if path specifies an existing directory"
| result |
result := CfsStat stat: 'aDir'.
result isCfsError
ifTrue: [|false]
ifFalse: [|result isDir].

Tip: Do not use stat: to try to prevent errors by testing whether a file exists before
opening it. It is better to catch the fail case from the open operation, because
the file can be deleted in the time between the existence test and opening the
file.

Mixing platform-specific and Common File System operations


It can sometimes be necessary or desirable to use platform-specific calls for some
file system operations, despite the fact that this will result in nonportable code.
Services are provided to permit cooperation between Common File System and
platform-specific calls, thereby minimizing the portability costs associated with
platform-specific calls.

Tip: Mixing Common File System and platform-specific operations should only be
done as a last resort when no satisfactory solution exists using portable
Common File System operations. Use these facilities with extreme caution.
These operations should only be used by experts, because there are hidden pitfalls.
Some platforms can have multiple valid file descriptor representations, thus some
calls might not be compatible with the particular representation chosen by the
CfsFileDescriptor. Furthermore, on such platforms, the possibility exists that the
underlying representation might change.

Performing a platform-specific call with a CfsFileDescriptor


To perform a platform-specific call with an existing open CfsFileDescriptor instance,
send the message fileDescriptor to the instance. This will answer the
platform-specific file descriptor used as the underlying representation of the
CfsFileDescriptor, which can then be passed as an argument to the platform-specific
call.

Converting a platform file descriptor into a CfsFileDescriptor


To convert a platform-specific file descriptor into a CfsFileDescriptor instance, use
the on: message as follows:
CfsFileDescriptor on: aPlatformFileDescriptor

This answers an initialized CfsFileDescriptor instance whose platform-specific file


descriptor is aPlatformFileDescriptor.

Some platforms can have more than one type of file handle. Ensure that the
platform-specific file descriptor specified as the argument to on: is of the same type
as that used by the Common File System itself.

Obtaining platform-specific error information


Platform-specific error information can be obtained from a CfsError instance by
means of the following messages. These messages return nil if not supported by a
particular platform or in the case of a synthetic error that is simulated for
compatibility rather than being generated by the platform operating system itself.
platformErrno
An integer representing the native platform error code, if any

76 IBM Smalltalk: Programmers Reference


platformErrorCategory
An integer representing the native platform error category, if any
platformErrorLocation
An integer representing the native platform error location, if any
platformRecommendedAction
An integer representing the recommended action, if any, suggested by the
platform operating system

Platform-specific error information is useful for debugging, and appears as four


integers, separated by commas, between square brackets when a CfsError prints
itself. If necessary, these methods can be used to trigger special platform-specific
actions. However, this will of course result in nonportable code.

Chapter 5. Common File System 77


78 IBM Smalltalk: Programmers Reference
Chapter 6. Common Graphics
The Common Graphics (CG) subsystem can be divided into two sets of classes:
v The core Common Graphics classes
v Classes comprising Common Graphics Image Support

This document first describes the core Common Graphics classes and later
describes the Image Support classes.

The core Common Graphics classes and methods enable programmers to do the
following:
v Define drawing attributes, such as line widths or styles, through the use of
graphics contexts
v Perform drawing operations, such as drawing lines, arcs, text strings, and
polygons
v Manipulate fonts
v Manipulate two-color bitmaps and multicolor pixmaps

Common Graphics Image Support also enables programmers to do the following:


v Specify a palette of colors to use in drawing operations
v Create, manipulate, and draw bitmap images in a platform-independent and
device-independent format
v Create and draw icons
v Load and unload images and icons using standard file formats

This chapter describes how the core system is based on the X Window Systems
graphics library, shows the class hierarchy, and explains the basic process for
building an application. It summarizes resource management, describing when and
how to free graphics resources that have been allocated.

Classes in the Common Graphics hierarchy are prefixed by the letters Cg; for
example, CgRGBColor or CgFont.

X Window system graphics library compatibility


The core Common Graphics subsystem is based on the X Window systems library
of graphics operations (Xlib) and provides the same functions as the Xlib C calls.
The Common Graphics Image Support classes are not based on Xlib.

This section will be of interest to developers familiar with Xlib graphic operations,
as it describes the two main strategies that were used to convert the standard Xlib
C calls to Smalltalk classes and methods. By understanding these two principles,
experienced Xlib software engineers should be able to use the Common Graphics
subsystem relatively quickly.

First, objects have been created for all C data types. These data types include items
such as Display, Drawable, GC, and Font. These objects were named by prefixing
the Xlib data type name with Cg and removing any leading X. For example, the
Xlib type Display becomes the Smalltalk class CgDisplay.

Copyright IBM Corp. 1994, 2000 79


Second, each Xlib C function has been converted to a corresponding Smalltalk
method. To understand this conversion, consider the following standard Xlib
function XDrawRectangle:
XDrawRectangle(display, drawable, gc, x, y, width, height);

In the Common Graphics subsystem, this function has been converted to the
following method:
CgDrawable>>#drawRectangle: gc x: x y: y width: width height: height.

In translating C Xlib functions to Smalltalk, two rules have been applied:


v If the first parameter of the C function is a display and the second parameter is
a graphics resource, such as a drawable or a graphics context, the second
parameter indicates the receiver of the equivalent Smalltalk message. Graphics
resources such as a CgDrawable know the display on which they are located, so
it is not necessary to specify the display in the Smalltalk equivalent. If the
second parameter is not a graphics resource, the first parameter is used to
indicate the receiver.
v The Smalltalk message selector is formed using the C function name, with the
preceding X removed, and the remaining parameter names.

Core Common Graphics class hierarchy


The core Common Graphics subsystem contains several classes. The most
frequently used classes are presented first. Frequently used classes are covered in
different subsections of this chapter, and are listed in Table 14.
Table 14. Frequently used classes
Class Represents Default object, if it exists See subsection
CgCursor* A cursor Varies by platform Using cursors on page 103
CgDisplay A connection to the CgDisplay default Core Common Graphics class
graphics server hierarchy on page 80
CgDrawable* A place to draw CgScreen default rootWindow or Core Common Graphics class
(either a window or a CgWindow default hierarchy on page 80
pixmap)
CgFont* A font resource CgDisplay default defaultFont or Using fonts on page 93
CgFont default
CgFontStruct A font structure CgDisplay default Using fonts on page 93
defaultFontStruct or
CgFontStruct default
CgGC (graphics A table of graphic CgScreen default defaultGC or Using graphics contexts on
context) display attributes CgGC default page 84
such as line width,
line style
CgGCValues A data structure used Table 16 on page 86 Using graphics contexts on
for configuring a page 84
CgGC
CgPixmap* An off-screen None Using pixmaps on page 106
rectangular array of
pixels (monochrome
or color)
CgWindow* A window CgScreen default rootWindow or Core Common Graphics class
CgWindow default hierarchy on page 80

80 IBM Smalltalk: Programmers Reference


Table 14. Frequently used classes (continued)
Class Represents Default object, if it exists See subsection
CgScreen A physical screen CgScreen default Core Common Graphics class
device hierarchy on page 80
CgTextItem A text data structure None Using fonts on page 93
Note: *Denotes a class that is a subclass of the abstract class CgId.

The following figure illustrates the most frequently used core Common Graphics
classes.

CgDisplay represents the link between your application


and the graphics server.

CgScreen represents the physical display device.

CgWindow represents an individual window. CgWindow


is the link to Common Widgets (see Tip below).

CgPixmap represents off-screen memory, located at


the graphics server, which can be drawn on and copied
to and from other windows or pixmaps.
CgGC represents the drawing attributes used in
drawing operations, including line width, line style,
fill pattern, and clipping region.

A CgDisplay object represents a connection to a graphics server: the link between


your application and the server. Your application can control several display
objects, each one representing a link between your application and a different
graphics server. You can obtain the default instance using CgDisplay default.

A CgScreen object represents a single hardware output device. It contains


information about the device including its dimensions, its color capabilities and its
default graphics context. You can obtain the default screen of the default display
using CgDisplay default defaultScreen or CgScreen default.

A CgDrawable object represents an area that can be drawn uponeither an instance


of CgWindow or CgPixmap. All drawing operations are performed on a drawable.
Conceptually, drawables are located in the graphics server. Drawing commands are
sent from the application to the graphics server, which then performs the required
operations on the drawable. Drawing operations are performed with respect to the
origin (the point defined by 0@0), which is in the upper left corner of the
drawable.

A CgWindow object represents an area of the screen, usually the visible area of a
user interface widget. Widgets are created by the application using the Common
Widgets subsystem (see the Common Widgets chapter). Each widget has a
corresponding window, which is obtained by sending the window message to the
widget. In addition to the windows of widgets, each screen has a root window
covering the entire area of the screen. All other windows are layered on top of the

Chapter 6. Common Graphics 81


root window. You can obtain the root window of the default screen using
CgWindow default. In general, IBM Smalltalk applications use windows provided by
the Common Widgets subsystem. The root window is useful for testing purposes
and is used throughout this chapter to show examples.

Note: It is important to note that CgWindow forms a link between the Common
Graphics and Common Widgets subsystems of IBM Smalltalk. A CwWidget
cannot be drawn on directly. However, every CwWidget has a corresponding
CgWindow that can be used for drawing. That is, CgWindows are also
CgDrawables. This is obtained by sending the window message to the
CwWidget. For further information, see Drawing area widgets on page 164
in the Common Widgets chapter.
A CgPixmap object represents an off-screen monochrome or color bitmapped image
created by your application. The format of pixmap memory is device dependent
and is not directly accessible to the application. Pixmaps are used to specify stipple
and tile pattern drawing attributes. They are also often used to cache the results of
drawing for high speed display. A pixmap is displayed by copying a rectangular
area of its contents to a window. Your application is responsible for creating,
manipulating, and freeing pixmap objects. These operations are described in
Using pixmaps on page 106.

A CgGC object (also called a GC or Graphics Context) is a data type that contains
drawing attribute information. The GC manages attributes such as the current
foreground color, line width, and line style. Requests to change the value of an
attribute are made to the GC. Your application is responsible for creating,
manipulating, and freeing GC objects. In general, applications will create their own
GCs. However, each screen has a default GC that is useful for testing purposes.
You can obtain the default GC of the default screen using CgGC default.

Each of these objects plays an important role in the Common Graphics subsystem.

Seldom-used and abstract classes


Once you are familiar with the frequently used classes and the classes that perform
specific operations, you might want to learn more about the seldom-used classes
listed in the following table. These classes are beyond the scope of this
introduction, but are referenced in later sections of this chapter.
Table 15. Seldom-used or abstract classes
Class Description
CgArc Specifies the integer coordinates and angles of an arc.
CgCharStruct Specifies properties of individual characters, including
height, width, and offset.
CgFontProp Specifies an extra property of a font such as its name or
its spacing.
CgID Abstract class of server resources, including
CgDrawable, CgCursor, CgFont.
CgLogicalFontDescription Specifies a data structure used to assist in parsing font
names.
CgRegion Specifies a geometric region consisting of a set of
rectangles.
CgSegment Specifies the integer coordinates of a line segment.
CgVisual Specifies visual properties of a screen.

82 IBM Smalltalk: Programmers Reference


A simplified drawing process overview
Most drawing operations use the following procedure:

Before drawing
v Load any required fonts.
v Create one or more graphics contexts.
v Create any required pixmaps.

Note: These steps cause operating system resources to be allocated.

During drawing
v Set attributes in the graphics context; for example, line width, font.
v Issue the drawing operation request; for example, drawString, fillRectangle.

After drawing
v Unload any fonts that were loaded.
v Free any graphics contexts that were created.
v Free any pixmaps that were created.

Note: These steps release the operating system resources.

The above process does not address the use of color. The sections on the Image
Support classes discuss the use of color. The remaining sections and examples for
the core classes assume drawing is done using black and white.

A simple example of the drawing process


In the following example, the default window is assigned to the temporary
variable drawable. A graphics context called gc is created for this drawable with no
initial values. Notice that the CgConstants pool dictionary constant None is used to
indicate that no values are being set. In the next line, the foreground color is set to
black. (The blackPixel message, used to get the pixel value for black, is discussed in
Drawing in black and white on page 119.) The drawable is then sent a message
to draw a filled rectangle at the appropriate coordinates using the display
attributes from the graphics context called gc. At the end of this routine, the GC is
freed, and its resources are returned to the operating system.
| gc drawable |
drawable := CgWindow default.
gc := CqGCdefault
"Set a graphics context attribute."
gc setForeground: drawable blackPixel.
drawable
"Issue a drawing operation."
fillRectangle: gc
x: 100
y: 100
width: 50
height: 50.

CgConstants pool dictionary


The Common Graphics subsystem uses a pool dictionary called CgConstants. This
dictionary provides pool variables for constant values that are used by the
subsystem. For example, pool variables such as GCForeground and GCLineWidth are
used to indicate which graphics context attributes to change in the createGC:values:
and changeGC:values: methods. Other pool variables, such as FillStippled and

Chapter 6. Common Graphics 83


LineSolid, are used to represent various GC attribute values. Pool variable names
should be used rather than directly using their constant values.

Using graphics contexts


A Graphics Context or GC is an object that contains drawing attribute
information, such as foreground color, background color, line width, and line style.
When an object is drawn on a drawable, the drawable references the graphics
context to determine how the object should appear.

Normally, an application creates and uses at least one GC. Your application can
create and use many graphics contexts, however. You should use multiple graphics
contexts carefully as they are relatively expensive to build and maintain.

For optimum performance, you should draw as much as possible with the same
GC, minimizing the number of times you change its components. The cost of
changing GC components relative to using different GCs depends on the specific
platform because some servers cache a limited number of GCs in the display
hardware.

For interactive experimentation and testing, there is a default GC that can be


accessed using CgScreen default defaultGC or CgGC default.

Note: The default GC is intended only for experimentation and testing.


Applications should create their own GCs.

In the following figure, a drawable (the root window of the default screen) receives
a message to draw a 100x100 filled rectangle at 20@20. The programmer provides a
graphics context called aGC. The drawable references aGC to find out how to
display the rectangle.

The graphics context called aGC Smalltalk message sent by your application

lineWidth 2 pixels
lineStyle LineSolid
foreground black
color

background white
color
fillStyle FillSolid Draws rectangle
. .
. .
. .

The root window (CgWindow default)

Basic graphics context methods


A brief summary of the main methods that apply to graphics contexts follows.
There are convenience methods for setting individual attributes of a graphics
context. These methods are described later in this section.

84 IBM Smalltalk: Programmers Reference


createGC:values:
Create and initialize a graphics context.
changeGC:values:
Change a group of graphics context attributes.
copyGC:dest:
Copy a group of graphics context attributes to another graphics context.
getGCValues:valuesReturn:
Retrieve a group of graphics context attributes from a graphics context.
freeGC Free the resources allocated to the graphics context.

Creating graphics contexts


Graphics contexts are created by sending the createGC:values: message to a
CgDrawable. The GC attributes are initialized to preset values, listed in Table 16 on
page 86. The two arguments of the message specify modifications to the preset
values. The first argument, valuemask, specifies which components of the second
argument, values, should override the preset values.

Creating a graphics context with default values


In the following example, the temporary variable gc is assigned a graphics context.
By passing the CgConstants constant None as the valuemask and nil as the values,
the graphics context is initialized using default values.
| gc |
gc := CgWindow default
createGC: None
values: nil.

Creating a graphics context using the CgGCValues object


Graphics contexts can be configured using a CgGCValues object. The CgGCValues
object is a convenient way of storing graphics attributes without having to store
the graphics context and its associated overhead. The CgGCValues object is used to
configure a GC or to query its attributes.

In the following example, a CgGCValues object is created, the function: message sets
the desired raster operation (GXhighlight), and the lineWidth: message sets a line
width value (2 pixels) in the CgGCValues object. The CgGCValues object is then
passed to the drawable using the createGC:values: message. The valuemasks
GCFunction and GCLineWidth are ORed together to create a mask that identifies
which attributes are being modified. The gcValues variable contains the new values
for the attributes.
| gc gcValues |
gcValues := CgGCValues new
function: GXhighlight;
lineWidth: 2.
gc := CgWindow default
createGC: GCFunction | GCLineWidth
values: gcValues.

Retrieving several values from a graphics context using the


CgGCValues object
You can also use a CgGCValues object to retrieve attribute values from a graphics
context. In the following example, the GCLineWidth valuemask is used to retrieve
the current line width. The information is returned in a new CgGCValues object.

Chapter 6. Common Graphics 85


| gcValues lineWidth |
CgGC default
getGCValues: GCLineWidth
valuesReturn: (gcValues := CgGCValues new).
lineWidth := gcValues lineWidth.

Note: The GCClipMask and GCDashList components of a GC cannot be retrieved.


Table 16 contains the complete list of attributes that can be initialized or modified.
In most cases, the attribute is either an integer value or a constant from the
CgConstants pool dictionary. For a detailed explanation, consult the method
comment for the createGC:values: method of CgDrawable.
Table 16. Graphics context attributes
Value mask Value expected Default value
GCForeground specifies the Integer pixel value (index of color in colormap) 0
foreground drawing color
GCBackground specifies the Integer pixel value (index of color in colormap) 1
background drawing color
GCLineWidth specifies the Integer value of line width in pixels 1 pixel
thickness of the drawing line
GCLineStyle specifies how a LineSolid (foreground LineSolid
line should be drawn. Dash color)
pattern is specified using a
dash list (see dashes below) LineOnOffDash
(foreground color)

LineDoubleDash (fore-
and background color)
GCCapStyle specifies how a CapNotLast CapButt
line should be ended CapButt
CapRound
CapProjecting
CGJoinStyle specifies how two JoinMiter JoinMiter
lines should be joined
JoinRound

JoinBevel

GCFillStyle specifies how to fill FillSolid FillSolid


an object such as an arc, (foreground color)
rectangle, or polygon FillStippled
(foreground color)
FillOpaqueStippled
(fore- and background
Pixmap
colors)
FillTiled
(a multicolored pixmap)
GCFillRule specifies how a EvenOddRule (hollow EvenOddRule
polygon should be drawn where areas intersect)
when it intersects itself
WindingRule (solid where
areas intersect)

86 IBM Smalltalk: Programmers Reference


Table 16. Graphics context attributes (continued)
Value mask Value expected Default value
GCArcMode specifies how an ArcChord (connects start ArcChord
arc should be drawn. To fill an and end points directly)
arc, the GCFillStyle would also
have to be set correctly. ArcPieslice (connects start
and end points through
the arc center point)

GCFunction specifies the raster GXcopy GXcopy


operation to be used (draws shape directly from source)
GXhighlight
(draws shape, replacing foreground with
background and vice-versa. Drawing
the same shape again will restore
drawable to previous state)
GCFont specifies the font to be A CgFont Standard system font
used in text drawing
operations
GCSubwindowMode specifies ClipByChildren ClipByChildren
whether drawing in a parent (allows drawing only in the parent window; child
window is clipped by or draws windows are not affected)
over child windows IncludeInferiors
(child boundaries are ignored; drawing in the
parent window will draw over child windows)
GCTileStripXOrigin specifies Integer value Checker stipple pattern 0
is 10 pixels square
the x offset of the tile or
Area to be filled is also
stipple pattern allowing you to 10 pixels square

position the pattern evenly If GCTileStipXOrigin


equals -1, the pattern is
offset as shown.

GCTileStipYOrigin specifies Integer value Checker stipple pattern is 0


10 pixels square
the y offset of the stipple or Area to be filled is also 10
pixels square
tile pattern allowing you to
position the pattern evenly If GCTileStipYOrigin equals
-1, the pattern is offset as
shown.

GCClipXOrigin x integer coordinate for clipping 0


GCClipYOrigin y integer coordinate for clipping 0
GCClipMask specifies the A CgPixmap of depth 1, or nil nil
CgPixmap used as a clipping
mask
GCDashOffset specifies in Integer value 0
pixels where to begin within a
dash pattern
tile specifies a CgPixmap to be A CgPixmap of the same depth as the drawable A tile CgPixmap
used as a multiplane, containing all zeros
multicolor fill pattern
stipple specifies a CgPixmap to A CgPixmap of depth 1 A stipple CgPixmap
be used as a 1-plane, 2-color containing all ones
fill pattern
dashes specifies the lengths of Array of integers #(4 4)
segments in the dash list specifying the length of
each dash in number of
pixels

Chapter 6. Common Graphics 87


Tip: Some platforms impose constraints on the implementation of the graphics
context attributes. These limitations are described in Appendix D. Common
graphics platform differences on page 515.

Configuring a graphics context using convenience methods


Convenience (set) methods have been provided for setting graphics context
attributes. These methods are sent directly to the graphics context and are simpler
to use than specifying CgGCValues and valuemasks.

In the following example, a GC is created using default values, then immediately


modified using two convenience methods.
| gc |
gc := CgWindow default
createGC: None
values: nil.
gc setFunction: GXcopy.
gc
setLineAttributes: 2
lineStyle: LineSolid
capStyle: CapButt
joinStyle: JoinMiter.

The following table lists frequently used convenience methods that can be sent to a
graphics context.
Table 17. Graphics context convenience (set) methods
Method Values modified Example
setBackground: Background color gc setBackground:
CgWindow default whitePixel
setDashes: Dash offset and pattern gc setDashes: 0 dashList: #(100 20)
dashList:
setForeground: Foreground color gc setForeground:
CgWindow default blackPixel
setFunction: Raster operation gc setFunction: GXhighlight
setLineAttributes: Width, style, endcap style, and gc
lineStyle: join style of a line, all at once setLineAttributes: 2
capStyle: lineStyle: LineOnOffDash
joinStyle: capStyle: CapButt
joinStyle: JoinMiter
setState: Foreground and background gc
background: colors, the raster operation setState: CgWindow default blackPixel
function: function, and the planeMask all background: CgWindow default whitePixel
planeMask: at once function: GXcopy
planeMask: nil

Copying graphics contexts


Copy graphics context attributes to another GC using copyGC:dest:. The following
example creates a GC, sets its foreground and background attributes, and then
copies these attributes to a new GC.
| oldGC newGC |
oldGC := CgWindow default
createGC: None
values: nil.

88 IBM Smalltalk: Programmers Reference


oldGC
setForeground: CgWindow default blackPixel;
setBackground: CgWindow default whitePixel.
newGC := CgWindow default
createGC: None
values: nil.
oldGC
copyGC: GCForeground | GCBackground
dest: newGC

Changing graphics contexts


Several graphics context attributes can be modified at once using the
changeGC:values: message. The following example creates a GC with default
attributes, then changes the foreground and background colors.
| gc |
gc := CgWindow default
createGC: None
values: nil.
gc
changeGC: GCForeground | GCBackground
values:
(CgGCValues new
foreground: CgWindow default blackPixel;
background: CgWindow default whitePixel).

Freeing graphics contexts


Graphics contexts are released using freeGC. Your application is responsible for
freeing the graphics contexts it creates. Graphics contexts can be freed at any time,
but cannot be used for drawing after being freed.
| gc |
gc := CgWindow default
createGC: None
values: nil.

"Perform some drawing operations using the gc..."

gc freeGC.

Tip: Do not attempt to free the default GC. This can cause unpredictable behavior.

Using graphics contexts with other drawables


A graphics context can be used for drawing on any drawable that is on the same
screen and that has the same depth as the drawable for which it was created. This
means that a GC created for a window on a 256-color screen can be used for any
window or 256-color pixmap on the same screen, but it cannot be used for
drawing on a pixmap of depth 1. In this case, a separate GC must be created for
the 1-bit pixmap.

Drawing operations
Common Graphics includes methods for drawing points, lines, rectangles,
polygons, arcs, and strings. All drawing methods are sent to a CgDrawableeither
a window or a pixmap. The first keyword in the method describes the function of
the method. The first argument passed to the method is always a graphics context.
The first keywords of the CG drawing methods follow:

drawArc:
drawArcs:

Chapter 6. Common Graphics 89


drawImageString:
drawLine:
drawLines:
drawPoint:
drawPoints:
drawRectangle:
drawRectangles:
drawSegments:
drawString:
drawText:
fillArc:
fillArcs:
fillPolygon:
fillRectangle:
fillRectangles:

Because the drawString:, drawImageString:, and drawText: methods require the use of
fonts, they are described in the subsection Using fonts on page 93. The other
drawing methods are covered in the following subsections:

Drawing points
Two methods are used to draw points. A point is a single pixel in a drawable.
drawPoint:x:y:
Draws a single point
drawPoints:points:mode:
Draws multiple points

Note: The point 0@0 is in the upper left corner.

The following example draws a point at 20@20 on the default root window using
the default GC.
CgWindow default
drawPoint: CgGC default
x: 20
y: 20.

The following example draws a series of points, specified as an Array of Points.


The constant CoordModeOrigin is used to indicate that each point should be drawn
relative to the origin. Passing CoordModePrevious as the mode specifies that the
coordinates of each point (except the first) are relative to the previous point.
| points |
points := Array new: 10.
1 to: 10 do: [:i |
points at: i put: (i*3)@(i*6)].

CgWindow default
drawPoints: CgGC default
points: points
mode: CoordModeOrigin.

Drawing lines
Three methods are used to draw lines. Lines provided in the argument list are
drawn using the attributes (such as color, line style, line width) contained in the
GC provided in the argument list.
drawLine:x1:y1:x2:y2:
Draws a single line.

90 IBM Smalltalk: Programmers Reference


drawLines:points:mode:
Draws a polyline, consisting of multiple connected lines specified as an
Array of Points.
drawSegments:segments:
Draws multiple, unconnected lines.

The following example draws a single line from 20@20 to 50@50 on the default
root window using the default GC.

CgWindow default
drawLine: CgGC default +

x1: 20 +

y1: 20
x2: 50
y2: 50.

The following example draws a series of lines on the root window beginning with
the first point and ending at the last point in the array.

| points |
(points := Array new: 6) +
at: 1 put: 89 @ 100;
at: 2 put: 12 @ 45; + +
at: 3 put: 108 @ 45;
at: 4 put: 31 @ 100; + +
at: 5 put: 60 @ 10;
at: 6 put: 89 @ 100.
CgWindow default
drawLines: CgGC default
points: points
mode: CoordModeOrigin

Drawing rectangles
The drawRectangle:x:y:width:height:, fillRectangle:x:y:width:height:,
drawRectangles:rectangles: and fillRectangles:rectangles: messages are used to draw
outlined and filled rectangles. The fillRectangle: and fillRectangles: messages are
shown in the following example:

CgWindow default
fillRectangle: CgGC default
x: 10 +
y: 10
width: 50 +
height: 50.

| rects rect1 rect2 |


rect1 := 10@10 extent: 50@50. + +
rect2 := 70@10 extent: 50@50.
rects := Array with: rect1 with: rect2. +

CgWindow default
fillRectangles: CgGC default
rectangles: rects.

Chapter 6. Common Graphics 91


Drawing polygons
Polygons are drawn using the fillPolygon:points:shape:mode: method. The polygon is
automatically closed whether or not the first and last points match. For instance, in
the following example, Array with: 10@10 with: 10@60 with: 60@35 with: 10@10
would have provided the same result. The shape argument describes the shape of
the polygon and can be set to Complex, Convex, or Nonconvex. Complex specifies that
the polygon can be either convex or self-overlapping. Convex specifies that the
polygon is one where a straight line connecting any two points of the polygon
does not cross any edge. Nonconvex specifies that the polygon is not convex, but
that it does not overlap itself. Specifying Complex will draw correctly in all cases.
Specifying Convex or Nonconvex can improve drawing performance on some
platforms, but the polygons shape must be known in advance. The following
example draws a triangle based on the collection of points provided.

| points | +
points := Array with: 10@10 with: 10@60
with: 60@35.
CgWindow default +
fillPolygon: CgGC default
points: points
shape: Convex +
mode: CoordModeOrigin.

The mode argument can be set to CoordModeOrigin, so that all points are drawn
relative to the origin, as in the previous example, or CoordModePrevious, so that all
points are drawn relative to the previous point in the collection, as in the following
example:

| points | +
points := Array with: 10@10
with: 10@60 with: 60@35.
CgWindow default
fillPolygon: CgGC default
points: points
+
shape: Convex
mode: CoordModePrevious +

Drawing arcs and circles


There are several methods for drawing arcs: fillArcs:arcs:,
drawArc:x:y:width:height:angle1:angle2:, fillArc:x:y:width:height:angle1:angle2:, and
drawArcs:arcs:. Angles are specified in 1/64 of a degree. Zero degrees is positioned
at three oclock with positive angles measuring in a counterclockwise direction. To
draw an arc, you specify a bounding rectangle, a start angle (in 64ths of a degree)
and an extent angle (in 64ths of a degree). The extent angle is relative to the start
angle. For example, to draw a semicircle from 90 degrees to 270 degrees, the start
angle would be 90 * 64 degrees and the extent angle would be 180 * 64 degrees.
You can draw multiple arcs using the drawArcs:arcs: methods.

The following example draws an arc bounded by the rectangle (20@20 corner:
60@60). The arc starts at an angle of 45 degrees and extends for another 270
degrees, ending at 315 degrees.

92 IBM Smalltalk: Programmers Reference


CgWindow default
drawArc: CgGC default
x: 20 +
y: 20
width: 40
height: 40
angle1: 64 * 45 +
angle2: 64 * 270

Drawing pie slices and chords using filled arcs


Filled arcs draw either as a pie slice or as a chord, depending on the setting of the
GCArcMode GC attribute. When the GCArcMode is set to ArcPieSlice, a pie slice is
drawn.

| gc |
gc := CgGC default.
gc setArcMode: ArcPieSlice. +
CgWindow default
fillArc: gc
x: 20
y: 20 +
width: 40
height: 40
angle1: 64 * 45
angle2: 64 * 270.

When the GCArcMode is set to ArcChord, a chord is drawn.

| gc |
gc := CgGC default.
gc setArcMode: ArcChord. +
CgWindow default
fillArc: gc
x: 20
y: 20 +
width: 40
height: 40
angle1: 64 * 45
angle2: 64 * 270.

Using fonts
This section gives an overview of the Common Graphics font system. For more
details, refer to the X11R4 reference manual. For further information on scalable
fonts, refer to the X11R5 reference manual. The following classes comprise the font
system:
CgFont
Represents a loaded font with a given size and style
CgFontStruct
Describes a CgFont. Contains information such as the number of characters
in the font, font height, average character width, ascender height,
descender height, whether the font is monospaced or proportional, and the
dimensions of each character in the font

Chapter 6. Common Graphics 93


CgFontProp
Contains additional font properties, such as the fonts name
CgCharStruct
Describes the dimensions of each character in the font
CgGC Specifies the font to use in string drawing operations
CgDisplay
Loads and lists available fonts
CgTextItem
Is a data structure used by the drawText: method, which draws multiple
text strings, each with different attributes, in a single drawing operation
CgLogicalFontDescription
Is a data structure used to assist in specifying and parsing font names

A simplified view of the font process


Each platform has a list of fonts that are available for use. An application queries
the platform to find out which fonts are available using a pattern-matching search
mechanism. The platform returns all matching font names. The application then
loads the appropriate font into memory, assigns the font to a graphics context and
issues string drawing operations. After the application is finished with the font, it
unloads it from memory.

CgFont
Handle to Your Application
aCgGC
the font
structure aCgWindow
Font: aCgFont drawString: aCgGC
x: 20
y: 100
string: Times
CgCharStruct
Individual
character
definition
CgFontStruct
aCgWindow
Font
attributes:
CgFontProp height, Times
XLFD standard width, #chrs,
and so on
font name

Using fonts
1. Query the system for available fonts (CgDisplay).
2. Load the requested font(s) into memory (CgDisplay).
3. Assign the font for use in drawing operations (CgGC).
4. Perform the drawing operations (CgDrawable).
5. Free the font(s) from memory (CgFont).

The following example illustrates the complete font process described in the steps
above. Each step is described in detail in the subsections following the example.
| fontNames font gc |
"Query the system for the string names of two available fonts"
fontNames := CgDisplay default
listFonts: '*'
maxnames: 2.
"Use the font string name to load a font"
font := CgDisplay default loadFont: (fontNames at: 2).
gc := CgCGdefault

94 IBM Smalltalk: Programmers Reference


"Assign the font for use in drawing operations"
gc setFont: font.

CgWindow default
"Perform a string drawing operation"
drawString: gc
x: 10
y: 100
string: 'hello world'.
"Free the font"
font unloadFont.

Querying the system for fonts


The system is queried for available fonts by sending the listFonts:maxnames:
message to a CgDisplay. The message includes two arguments: a pattern-matching
string and the maximum number of font string names to be returned.

Pattern matching
The Common Graphics subsystem defines font names using the X Logical Font
Description (XLFD) convention. This convention is described in detail in the X11R4
and R5 reference manuals. Valid search patterns can be created using any
combination of wildcard (* or ?) or literal values. The * represents a string of
zero or more arbitrary characters. The ? represents a single arbitrary character.
The - is used as a separator between fields in the XLFD name. The following
example shows the meaning of each field in the XLFD name:

font set_width average


weight name pixel size x and y
foundry resolution width

-adobe-helvetica-bold-i-normal-sans serif-12-120-75-75-p-70-iso8859-1

family slant additional point spacing character set


name information size registry
(often blank)
The following table describes the fields of an XLFD name.
Table 18. X logical font description fields
Field name Valid field values Examples
Font foundry X-registered manufacturer name adobe, bitstream,
microsoft
Family name Font family name helvetica, times, courier
Weight Blackness or density medium, bold
Slant Vertical posture of font characters r (roman), i (italic), o
(oblique), ri (reverse
italic), ro (reverse
oblique), ot (other)
Set_width_name Width of font characters (usually normal, condensed,
normal) narrow, double wide
Add_style_name Additional information (often blank) serif, sans serif, informal,
decorated, iso9241
Pixel_size Font vertical height measurement in 8, 10, 12, 14, and so on
pixels (zero indicates a scalable
font)

Chapter 6. Common Graphics 95


Table 18. X logical font description fields (continued)
Field name Valid field values Examples
Point_size Font vertical height measurement in 120, 140, 180, 240, 360
1/10ths of 1/72 of an inch (24 point = (zero indicates a scalable
240) font)
Resolution_x Horizontal resolution of display in dots 75, 100
per inch
Resolution_y Vertical resolution of display in dots 75, 100
per inch
Spacing Font spacing p (proportional), m
(monospaced), c
(character cell)
Average_width Average width of all the glyphs in 1/10 50, 60, 70, and so on
of pixel increments. Used for (zero indicates a scalable
monospaced fonts and character cell font)
fonts to calculate characters per line
Charset_registry/ X registered name that identifies the iso8859-1
encoding coding authority for the character set

The following table shows the sample values for the character set and
registry/encoding fields.
Table 19. Example platform charset_registry/encoding names
Family Name Charset Registry/Encoding Description
courier iso8859 1 courier matches iso8859
specification
symbol microsoft fontspecific microsoft symbol font
@system microsoft @shiftjis microsoft system double-byte
font, double-byte characters
rotated 90 counter-clockwise
terminal microsoft shiftjis microsoft terminal double-byte
font double-byte characters not
rotated
courier ibm 437 ibm courier for code page 437
courier ibm 932.sbcs ibm courier single-byte font for
code page 932
mincho ibm 932 ibm mincho double-byte font
for code page 932 (zero
indicates a scalable font)

Note: The CdLogicalFontDescription class can be used to assist in creating XLFD


font names and in parsing the fields out of font names. Its use is illustrated
in Determining whether a font is scalable on page 97 and Parsing a
scalable font name on page 98.

The following example finds a maximum of two font strings of any description (*):
| fontNames |
fontNames := CgDisplay default
listFonts: '*'
maxnames: 2.

96 IBM Smalltalk: Programmers Reference


The following example finds a maximum of 20 helvetica fonts. Note that all
fourteen fields are specified, either as wildcards or not. It is not necessary to
specify all fourteen fields. However, if all fourteen fields are specified and either
the pixel size or point size fields are not wildcards (that is, a specific size is
desired), then scalable fonts will match the pattern, filling in the pixel size and
point size fields appropriately. Scalable fonts are described in detail in the next
section.
| fontNames |
fontNames := CgDisplay default
listFonts: '-*-helvetica-*-*-*-*-*-*-*-*-*-*-*-*'
maxnames: 20

Tip: The listFonts: method is not required to include scalable fonts in the returned
list unless all XLFD fields are specified in the pattern. To ensure that scalable
fonts are included in the list, specify all 14 XLFD fields as in the previous
example rather than using a single wildcard to match across several fields.

As a general rule, it is best to specify the fonts family name, weight, slant,
and point size fields explicitly.

It is better to match the point size field and leave the pixel field as a wildcard
because this allows the most appropriate match for the resolution of the
display.

Loading fonts
Common Graphics supports both scalable and nonscalable fonts. Scalable fonts are
a variable font definition that can be scaled to different sized fonts. Nonscalable
fonts cannot be scaled and, when loaded, are always the same size. Scalable fonts
are indicated by zeros in the pixel size, point size, and average width fields of the
font name.

To load a scalable font successfully, you must specify the size of the font by
changing the pixel size or the point size field in the font name prior to issuing one
of the font loading methods. Because a nonscalable font has a predetermined size,
its font name is passed directly to the font loading methods without any
preprocessing.

Determining whether a font is scalable


Consider the two font names that follow. In the first font name, the pixel size, point
size and average width fields contain zeros (-0-0-...-0-) indicating that it is a scalable
font. In the second font name, all three fields have actual values (-33-240-...-180-)
indicating that it is a nonscalable, or fixed size, font.
-adobe-helvetica-bold-i-normal-sans serif-0-0-100-100-p-0-iso8859-1
-adobe-helvetica-bold-i-normal-sans serif-33-240-100-100-p-180-iso8859-1

CgLogicalFontDescription class can be used to parse and modify an XLFD name. The
following example determines whether or not a font is scalable by first creating a
CgLogicalFontDescription object for the font.
| fontName description |
"Ask the system for any font name."
fontName := (CgDisplay default
listFonts: '-*-*-*-*-*-*-*-*-*-*-*-*-*-*'
maxnames: 1) first.

"Create a font description object for the font name."

Chapter 6. Common Graphics 97


description := CgLogicalFontDescription name: fontName.

"Answer true if the font is scalable, false otherwise."


|description isScalable.

Parsing a scalable font name


To load a scalable font, the zeros in the size fields of the font name must be
replaced by actual values and passed to one of the font loading methods. The
following example illustrates the use of a CgLogicalFontDescription object to specify
the point size in a scalable font name. Point size is specified in 1/10ths of 1/72 of
an inch (tenths of a point), so a 24-point font is represented as 240. Also note that
the pixel size and average width fields are changed to wildcards, indicating that
they will be computed based on the point size.
| fontName description |
fontName := '-adobe-helvetica-bold-i-normal-sans serif-0-0-100-100-p-0-iso8859-1'.

"Create a font description object for the name."


description := CgLogicalFontDescription name: fontName.
"Replace point size, pixel size, and average character width fields."
description
points: '240';
pixels: '*';
averageWidth: '*'.

"Get the modified name back as a string."


description name

The preceding code returns this:


'-adobe-helvetica-bold-i-normal-sans serif-*-240-100-100-p-*-iso8859-1'

Once the scalable font name has been modified to specify a fixed size, it is then
referred to as a scaled font name. It can then be passed to one of the font loading
methods that follow. Fonts are loaded into memory by sending the loadFont: or
loadQueryFont: messages to a CgDisplay object. The loadFont: method takes a font
name and returns a CgFont object. The loadQueryFont: method takes a font name
and returns a CgFontStruct object.

Loading a CgFont
| fontName font |
fontName := '8x13'.
font := CgDisplay default
loadFont: fontName.

Loading a CgFontStruct
| fontName fontStruct |
fontName := '8x13'.
fontStruct := CgDisplay default
loadQueryFont: fontName.

More about CgFont and CgFontStruct


In the last example, two different operations were used to load a CgFontStruct and
a CgFont object. These two objects, while both representing a font, provide different
kinds of information to the application.

CgFont is a handle to the font in the operating system. It is useful in applications


where you simply wish to draw text and do not require a measure of the
dimensions of the text.

98 IBM Smalltalk: Programmers Reference


CgFontStruct is a more detailed description of the font. It provides information that
is useful when you need to perform calculations based on text width or text
height, for example, centering a label in a window.

Here are some of the more useful CgFontStruct access methods:


font Returns the CgFont.
name Returns the name of the font.
ascent Returns the integer height in pixels of the font ascender.
descent Returns the integer height in pixels of the font descender.
height Returns the integer height in pixels of the font (ascent plus descent).
defaultChar
Returns the character index to use for undefined characters.
numChars
Returns the number of characters in the font.
perChar
Returns the first row of an array of CgCharStructs describing the
dimensions of each character in the font.
perCharAtRow:
Returns the specified row of an array of CgCharStructs describing the
dimensions of each character in the font. Use for double-byte fonts.
perCharNumRows
Returns the number of rows in the fonts per-char information.
perCharNumColumns
Returns the number of columns in the fonts per-char information.
maxBounds
Returns a CgCharStruct describing the maximum bounds over all characters
in the font.
minBounds
Returns a CgCharStruct describing the minimum bounds over all characters
in the font.
textWidth:
Returns the width in pixels of the text string provided.
textExtents:directionReturn:fontAscentReturn:fontDescentReturn:overallReturn:
Returns the direction in which text is drawn, the font ascender, the font
descender, and a CgCharStruct describing the overall size of the text.

More about CgCharStruct


While a CgFontStruct holds information about the entire font, a CgCharStruct holds
information about each individual character in the font. The following diagram
shows some of the commonly used metrics of both CgFontStruct and CgCharStruct

Chapter 6. Common Graphics 99


as they pertain to two characters A and j in a typical font.

rbearing
lbearing bearing

CgFontStruct CgFontStruct
ascent

CgCharStruct for A

ascent

CgCharStruct for j
ascent
baseline

descent
descent = 0
descent
character
origins

CgCharStruct for A CgCharStruct for j


width width

Note that the origin of each character is on the baseline, and not in the upper left.
A CgCharStructs origin is the leftmost pixel along its baseline; therefore, it is
actually at a point that is 0 @ ascent with respect to the upper left corner of the
CgCharStruct. This is useful to know when drawing a string inside of a rectangle
drawn at x @ y. The string must be drawn at x @ (y + ascent). This is in keeping
with typographic convention. The ascent, descent, and width of a CgFontStruct are
equal to the largest CgCharStruct ascent, descent, and width in the font. The
lbearing, rbearing, and bearing metrics are only shown for A. Additional
CgCharStruct instance methods follow:
ascent Returns the number of pixels between the baseline and the top edge of the
raster.
descent Returns the number of pixels between the baseline and the bottom edge of
the raster.
height Returns the number of pixels between the top edge of the raster and the
bottom edge of the raster.
extent Returns a point in which the x-coordinate is the characters height, and the
y-coordinate is the characters width.
bearing Returns the number of pixels between the left edge of the raster and the
right edge of the raster.
lbearing
Returns the number of pixels between the origin and the left edge of the
raster.
rbearing
Returns the number of pixels between the origin and the right edge of the
raster.
width Returns the number of pixels to advance to the next characters origin.

Assigning fonts for use in drawing operations


Before a font can be used in a drawing operation, it must be assigned to a graphics
context. All text drawing operations use the font in the graphics context. A CgFont
is assigned to the graphics context using the setFont: method.

100 IBM Smalltalk: Programmers Reference


| font |
font := CgDisplay default
loadFont: '-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*'.
CgGC default
setFont: font.

The following example shows assigning a CgFontStruct for use in a graphics


context:
| fontStruct |
fontStruct := CgDisplay default
loadQueryFont: '-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*'.
CgGC default
"The setFont: method expects to be passed a CgFont"
setFont: fontStruct font.

String drawing operations with fonts


You can draw text by sending the drawString:, drawImageString:, or drawText:
messages to a CgDrawable object. As in all drawing operations, the first parameter
passed to the method must be a valid graphics context. This graphics context will
contain either a default font or the font you have loaded and set in the graphics
context.

In all string and text drawing methods the y-coordinate specifies the coordinate
where the baseline of the string will be drawn, not the top of the strings bounding
box.

Drawing a string
The drawString:x:y:string: method draws a string at a given point in the foreground
color. Only the pixels for the characters are modified. Pixels between characters are
left unchanged.

CgWindow default
drawString: CgGC default
x: 10 +
y: 100
string: 'Going global'.

Drawing an image string


The drawImageString:x:y:string: method draws a string at a given point. The text is
drawn in the foreground color and the pixels in the remainder of the rectangle
containing the string are set to the background color.

CgWindow default
drawImageString: CgGC default
x: 10 +
y: 100
string: 'Hello world'.

Drawing multifont strings using CgTextItem objects


The drawText:x:y:items: allows multiple strings to be drawn at once. Strings are
drawn in the same manner as drawString:. The background is left unmodified. Each
string is specified by a CgTextItem object.

Chapter 6. Common Graphics 101


A CgTextItem object contains character, delta and font components. The char
component specifies the string value, delta represents the horizontal offset from the
previous item and font represents the font to be used in the drawing operation. The
font component is optional. If it is specified, the font in the graphics context is
changed and the string is drawn in the new font. Otherwise, the string is drawn in
the current font. The CgTextItem must be provided with at least the char and delta
information. These values can be set individually, with chars:, delta:, or font:, or
simultaneously, with chars:delta:font:.

| fontName1 font1 fontName2 font2 item1 item2 item3 |


fontName1 := (CgDisplay default
listFonts: '-*-helvetica-bold-r-normal-*-*-240-*-*-*-*-*-*' +The quick brown fox
maxnames: 1) at: 1.
font1 := CgDisplay default loadFont: fontName1.

fontName2 := (CgDisplay default


listFonts: '-*-helvetica-bold-i-normal-*-*-240-*-*-*-*-*-*'
maxnames: 1) at: 1.
font2 := CgDisplay default loadFont: fontName2.

item1 := CgTextItem chars: 'The ' delta: 0 font: font1.


item2 := CgTextItem chars: 'quick ' delta: 0 font: font2.
item3 := CgTextItem chars: 'brown fox' delta: 0 font: font1.

CgWindow default
drawText: CgGC default
x: 10
y: 100
items: (Array with: item1 with: item2 with: item3).

Releasing CgFonts and CgFontStructs from memory


You can release fonts or fontStructs from memory either by sending the unloadFont
message to a CgFont object or by sending the freeFont message to a CgFontStruct
object. This should be done when the font is no longer required for drawing.

Tip: Every font loaded with loadFont: must be unloaded using unloadFont. Every
fontStruct loaded with queryFont: or loadQueryFont: must be freed using
freeFont. The freeFont method also unloads the CgFont object associated with
the CgFontStruct.

Obtaining the current font from a graphics context


You can determine the font currently set in a graphics context using the
getGCValues: method. In the following example, the GCFont valuemask is used to
retrieve the current font. The information is returned in a new CgGCValues object.
You can retrieve the CgFontStruct information by sending a queryFont message to
the CgFont. You then obtain the actual string name of the font from the
CgFontStruct.
| values font fontStruct fontName |
CgGC default
getGCValues: GCFont
valuesReturn: (values := CgGCValues new).
font := values font.
fontStruct := font queryFont.
fontName := fontStruct name.

102 IBM Smalltalk: Programmers Reference


Using cursors
The Common Graphics subsystem provides three types of cursors. Font cursors are
created from a predefined, built-in cursor font. Glyph cursors are created from
characters in a specified font. Pixmap cursors are created from two pixmaps.

The process for using cursors


To use cursors, do the following.
1. Create the cursor.
2. Define the cursor in a window.
3. Perform some work, during which the cursor is shown. Whenever the mouse
pointer is over a window, the defined cursor for the window is shown.
4. Undefine the cursor from the window.
5. Free the cursor.
Before using a cursor, you must create it by sending a cursor creation message to
either a CgDisplay, CgFont, or CgPixmap object, depending on what type of cursor
(font, glyph, or pixmap) is being created. The method allocates the cursor and
returns a CgCursor object. Although there are different ways of creating cursors,
once created all cursors have the same behavior. See the subsections that follow for
more on creating cursors.

A cursor can be shown in a window by sending defineCursor: to the window,


specifying the cursor to use.

Tip: Cursors can only be defined in the window of a widget, not the root window
of the screen. The widget can be any widget and is not restricted to the shell
widget of the application.

The cursor is reset to the default cursor by sending undefineCursor to the window.
When the application is finished using the cursor, it should be released from
memory by sending freeCursor to the cursor. This process is illustrated by the
following code. (This example and the following cursor examples assume that the
shell method answers a CwTopLevelShell widget.)

| window watch |
window := self shell window.

"Create the cursor."


watch := window display
createFontCursor: XCWatch.

"Display the cursor."


window defineCursor: watch.
(Delay forSeconds: 5) wait.
window undefineCursor.
watch freeCursor

Font cursors
Font cursors are easy to use. They are specified by a constant from the CgConstants
pool dictionary. The previous example used a font cursor called watch:. In the
following example, a coffee mug cursor appears on the screen for 5 seconds:
| window mug |
window := self shell window.
mug := window display createFontCursor: XCCoffeeMug.

Chapter 6. Common Graphics 103


window defineCursor: mug.
(Delay forSeconds: 5) wait.
window undefineCursor.
mug freeCursor

The following table lists the available font cursors specified in the CgConstants pool
dictionary:
Table 20. Font cursors
XCArrow XCBasedArrowDown XCBasedArrowUp XCBoat
XCBogosity XCBottomLeftCorner XCBottomRightCorner XCBottomSide
XCBottomTee XCBoxSpiral XCCenterPtr XCCircle
XCClock XCCoffeeMug XCCross XCCrosshair
XCCrossReverse XCDiamondCross XCDot XCDotbox
XCDoubleArrow XCDraftLarge XCDraftSmall XCDrapedBox
XCExchange XCFleur XCGobbler XCGumby
XCHand1 XCHand2 XCHeart XCIcon
XCIronCross XCLeftbutton XCLeftPtr XCLeftSide
XCLeftTee XCLlAngle XCLrAngle XCMan
XCMiddlebutton XCMouse XCNumGlyphs XCPencil
XCPirate XCPlus XCQuestionArrow XCRightbutton
XCRightPtr XCRightSide XCRightTee XCRtlLogo
XCSailboat XCSbDownArrow XCSbHDoubleArrow XCSbLeftArrow
XCSbRightArrow XCSbUpArrow XCSbVDoubleArrow XCShuttle
XCSizing XCSpider XCSpraycan XCStar
XCTarget XCTcross XCTopLeftArrow XCTopLeftCorner
XCTopRightCorner XCTopSide XCTopTee XCTrek
XCUlAngle XCUmbrella XCUrAngle XCWatch
XCXCursor XCXterm

Glyph cursors
Glyph cursors are created by using a character from an application-specified font
as a cursor. To create a glyph cursor, the application must have loaded a CgFont.
Then a cursor object is created by sending the
createGlyphCursor:sourceChar:maskChar: or
createGlyphCursor:sourceChar:maskChar:foregroundColor:backgroundColor: message to
the CgFont object (the source font). In the following example, the same font and
character are used for the mask font and the mask character. The following code
creates and displays the @ sign as a cursor.

| window font cursor |


window := self shell window. @
"Create the cursor."
font := window display loadFont: '8x13'.
cursor := font
createGlyphCursor: font
sourceChar: $@ asInteger
maskChar: $@ asInteger.
font unloadFont.

"Display the cursor."


window defineCursor: cursor.
(Delay forSeconds: 5) wait.
window undefineCursor.
cursor freeCursor

104 IBM Smalltalk: Programmers Reference


Pixmap cursors
Pixmap cursors are created by specifying two pixmaps: a shape pixmap and a
mask pixmap. The pixmaps must both have a depth of 1 (that is, 1 bit per pixel).
The mask pixmap specifies at which pixels the cursor is opaque. Where the mask
pixmap is 0, the background shows through. Where it is 1, the shape pixmap
specifies the color of the pixel. Where the shape pixmap is 1, the foreground color
(black) is used. Where it is 0, the background color (white) is used. A mask pixmap
of nil specifies that the cursor is opaque at all pixels. For more information on
creating and using pixmaps, see Using pixmaps on page 106.

In the following example, a CgPixmap is created by sending the


createBitmapFromData:width:height: message to the drawable. Then this CgPixmap
object is sent the createPixmapCursor:x:y: or
createPixmapCursor:foregroundColor:backgroundColor:x:y: message. Parameters passed
to this method include an optional mask pixmap, and the x- and y-coordinates of
the cursors hot spot. The hot spot is the point at which the cursor is centered over
the current pointer coordinates.

The following code creates and displays a pixmap cursor with the shape of a small
black rectangle with a transparent interior. The cursors hot spot is in the center of
the rectangle. Note that, in this case, the same pixmap is used both for the mask
and to define the shape.

| window data pixmap cursor |


window := self shell window.

"Create the cursor."


data := #[
2r11111111
2r10000001
2r10000001
2r10000001
2r10000001
2r10000001
2r10000001
2r11111111].
pixmap := window
createBitmapFromData: data
width: 8
height: 8.
cursor := pixmap
createPixmapCursor: pixmap
x: 4
y: 4.
pixmap freePixmap.

"Display the cursor."


window defineCursor: cursor.
(Delay forSeconds: 5) wait.
window undefineCursor.
cursor freeCursor

Changing the color of a cursor


The color of a cursor can be changed by sending the recolorCursor:backgroundColor:
message to a CgCursor object. The color arguments are specified using CgRGBColor
objects. Class CgRGBColor is described in more detail in Specifying colors on
page 113. The following example illustrates how to recolor a font cursor so that the
foreground is red and the background is blue.

Chapter 6. Common Graphics 105


| window cursor red blue |
window := self shell window.

"Create and recolor the cursor."


cursor := CgDisplay default createFontCursor: XCWatch.
red := CgRGBColor red: 65535 green: 0 blue: 0.
blue := CgRGBColor red: 0 green: 0 blue: 65535.
cursor recolorCursor: red backgroundColor: blue.
"Display the cursor."
window defineCursor: cursor.
(Delay forSeconds: 5) wait.
window undefineCursor.
cursor freeCursor

Note: Some platforms, such as OS/2 PM and Windows, do not support color
cursors.

Platform cursors
You can map font cursors to correspond to a platforms built-in cursors by sending
mapFontCursor:platformCursor: to a CgDisplay object. In the following example, the
symbol #wait is defined as a key that maps to the current platforms wait cursor.
You can use any object as a key. The cursor is created using createFontCursor: and
displayed in a window. Note that constants from the PlatformConstants pool
dictionary are used.
| window platform key cursor |
window := self shell window.

"Determine what the current platform is and set the constant name
specifying the platform's wait cursor."
platform := System subsystemType: 'CG'.
(#('WIN' 'WIN32s' 'WIN-NT') includes: platform)
ifTrue: [key := 'IdcWait'].
platform = 'PM' ifTrue: [key := 'SptrWait'].
platform = 'X' ifTrue: [key := 'XCWatch'].
"Look up the constant and define a font cursor that maps to the
platform's wait cursor."
window display
mapFontCursor: #wait
platformCursor: (PlatformConstants at: key asPoolKey).

"Create a font cursor which maps to the platform's wait cursor."


cursor := window display
createFontCursor: #wait.
"Display the cursor."
window defineCursor: cursor.
(Delay forSeconds: 5) wait.
window undefineCursor.

"Free the cursor."


cursor freeCursor.

Using pixmaps
A pixmap, represented by class CgPixmap, is an off-screen rectangular area of
pixels, located in graphics server memory. The depth, or number of bits per pixel,
of a pixmap is specified when it is created. The depth is usually either 1 bit or the
depth of the window on which it will be displayed. Pixmaps with a depth of 1,
representing monochrome graphics, are called bitmaps, but are also represented by
class CgPixmap. The memory representation of pixmaps is device dependent and is
hidden from the application programmer. A pixmap is a kind of drawable,

106 IBM Smalltalk: Programmers Reference


meaning that it can be the destination of drawing operations. The following
methods are used to create, free, and manipulate pixmaps, excluding drawing
operations.
createPixmap
Creates a pixmap.
createPixmapFromBitmapData
Creates a pixmap and initialize it using the bitmap data provided.
freePixmap
Releases a pixmap from memory.
getGeometry
Gets the dimensions of the pixmap.
createBitmapFromData
Creates a bitmap from the data provided.
readBitmapFile
Reads a bitmap file from disk in text format.
writeBitmapFile
Writes a bitmap file to disk in text format.

Pixmaps are created by sending the createPixmap:height:depth: or


createPixmapFromBitmapData:width:height:fg:bg:depth: message to a CgDrawable. The
createPixmap: message specifies the width, height, and depth of the pixmap. A
depth of 1 specifies a monochrome pixmap (a bitmap); a depth of 8 specifies a
256-color pixmap. In most cases, the depth is set to either 1 or the depth of the
window the pixmap will be displayed on.

Creating a pixmap using createPixmap:


In this example, a pixmap is created with a width of 500, a height of 50, and a
depth equal to that of the root window. depth is a CgWindow accessor method that
returns the depth of the window.
| window pixmap |
window := CgWindow default.
pixmap := window
createPixmap: 500
height: 50
depth: window depth

The contents of a newly created pixmap are undefined.

Freeing pixmaps
The application is responsible for releasing pixmaps (including bitmaps) from
memory when they are no longer required. Depending on their dimensions, these
objects can take up large amounts of memory. Pixmaps are freed by sending the
freePixmap message to a CgPixmap object.
pixmap freePixmap

Creating a pixmap using createPixmapFromBitmapData:


In this example, a series of bits are passed to the pixmap creation routine along
with width, height, foreground pixel, background pixel, and depth information.
Wherever a 1 appears in the bitmap data, the foreground pixel is set in the
pixmap. Wherever a 0 appears, the background pixel is set. After creating the
pixmap, the example copies it onto the screen.

Chapter 6. Common Graphics 107


| gc window bits pixmap |
gc := CgGC default.
window := CgWindow default.
bits := #[
2r11111111 2r11111111
2r00000110 2r01100000
2r00011000 2r00011000
2r01100000 2r00000110
2r10000000 2r00000001].
pixmap := window
createPixmapFromBitmapData: bits
width: 16
height: 5
fg: window blackPixel
bg: window whitePixel
depth: window depth.
pixmap
copyArea: window
gc: gc
srcX: 0
srcY: 0
width: 16
height: 5
destX: 10
destY: 10.
pixmap freePixmap

Copying pixmaps to and from windows


The copyArea:gc:srcX:srcY:width:height:destX:destY: method enables the contents of
any CgDrawable object (window or pixmap) to be copied to another CgDrawable
object. The receiver is the source drawable. The first argument is the destination
drawable. The source and destination can be the same drawable. Additional
arguments include the graphics context, the source rectangle to be copied (srcX:,
srcY:, width:, and height:), and the location in the destination (destX: and destY:) to
begin the copying operation.

How the bits are displayed is dependent on the raster operation function, either
GXcopy or GXhighlight, that has been set in the graphics context. The raster
operation specifies a binary function that is applied at each pixel, using the current
pixel values of the source and the destination to determine the new pixel value of
the destination.

GXcopy sets the destination pixel value to the source pixel value; the current
destination pixel value is ignored.

GXhighlight functions differently when copying areas than when drawing using the
foreground or background CgGC components. In the latter case the foreground and
background colors are exchanged in the destination. When copying areas,
GXhighlight performs a binary exclusive-OR on the current source and destination
pixel values and sets the destination pixel value to the result. The example in the
following subsection illustrates copying a pixmap.

Getting pixmap geometry


Information about a CgDrawable (window or pixmap) can be obtained by sending
the getGeometry: message to the drawable. In the following example a pixmap is
created and its geometry is requested. Return parameters are provided for the root

108 IBM Smalltalk: Programmers Reference


window of the drawable, x, y, width, height, border width and depth information.
The root window of a pixmap is the same as that for the window that was used to
create the pixmap.
| window pixmap rootReturn xReturn yReturn wReturn hReturn bwReturn dReturn |
window := CgWindow default.
pixmap := window
createPixmap: 500
height: 50
depth: window depth.
pixmap
getGeometry: (rootReturn := ReturnParameter new)
xReturn: (xReturn := ReturnParameter new)
yReturn: (yReturn := ReturnParameter new)
widthReturn: (wReturn := ReturnParameter new)
heightReturn: (hReturn := ReturnParameter new)
borderWidthReturn: (bwReturn := ReturnParameter new)
depthReturn: (dReturn := ReturnParameter new).
Transcript cr;
show: 'Pixmap root: ', rootReturn value printString; cr;
show: ' x: ', xReturn value printString;
show: ' y: ', yReturn value printString;
show: ' width: ', wReturn value printString;
show: ' height: ', hReturn value printString; cr;
show: ' borderWidth: ', bwReturn value printString;
show: ' depth: ', dReturn value printString.
pixmap freePixmap

There are also convenience methods, width, height, and depth, for querying the
geometry of drawables.

Creating a bitmap from bitmap data


A CgPixmap of depth 1, called a bitmap, can be created and initialized from bitmap
data by sending the createBitmapFromData:with:height: message to a CgDrawable
(window or pixmap). The message arguments are the bitmap data, its width, and
its height. In the following example, an 8x8 bitmap is created using the data in bits.
The bitmap forms a brick pattern when several copies are drawn side by side.
| bits bitmap |
bits := #[
2r11111111
2r00000001
2r00000001
2r00000001
2r11111111
2r00010000
2r00010000
2r00010000].
bitmap := CgWindow default
createBitmapFromData: bits
width: 8
height: 8.

Creating stipples using bitmaps


To carry the example further, the bitmap in the previous example could be used to
create a stipple drawing pattern. The pattern (a brick pattern) is created as a
bitmap. This bitmap is set as the stipple pattern in a graphics context using the
setStipple: method. The fill style, FillOpaqueStippled, and the stipple origin offsets (x:
1 y: 2) are also set by sending the appropriate messages to the graphics context.
The graphics context is then used to draw a filled rectangle.

Chapter 6. Common Graphics 109


| bits bitmap |
bits := #[
2r11111111 2r00000001 2r00000001 2r00000001
2r11111111 2r00010000 2r00010000 2r00010000].
bitmap := CgWindow default
createBitmapFromData: bits
width: 8
height: 8.
CgGC default
setFillStyle: FillOpaqueStippled;
setTSOrigin: 1 tsYOrigin: 2;
setStipple: bitmap;
setForeground: CgWindow default blackPixel;
setBackground: CgWindow default whitePixel.
CgWindow default
fillRectangle: CgGC default
x: 50
y: 50
width: 100
height: 100.
CgGC default setFillStyle: FillSolid.
bitmap freePixmap.

Writing bitmaps to files


Bitmaps can be written to files by sending the
writeBitmapFile:width:height:xHot:yHot: message to the bitmap.

Tip: The bitmap file format only supports any CgPixmap of depth one. It is an
error to try to write a CgPixmap with a different depth.
The bitmap is written in X11 bitmap file format, which looks like C source code for
a structure initialization. Parameters passed to the method include the file name,
the width, the height, and the hot spot coordinates within the bitmap. A hot spot is
useful for cursor specifications. The write operation returns zero if successful, or an
error value if unsuccessful.
| bits bitmap error |
bits := #[
2r11111111 2r00000001 2r00000001 2r00000001
2r11111111 2r00010000 2r00010000 2r00010000].
bitmap := CgWindow default
createBitmapFromData: bits
width: 8
height: 8.
error := bitmap
writeBitmapFile: 'brick'
width: 8
height: 8
xHot: 0
yHot: 0.
error = 0
ifTrue: [Transcript cr; show: 'File write successful.']
ifFalse: [Transcript cr; show: 'File write error: ',
error printString].
bitmap freePixmap.

The file created by this example follows:


#define brick_width 8
#define brick_height 8
#define brick_x_hot 0
#define brick_y_hot 0
static char brick_bits[] = {
0xff, 0x01, 0x01, 0x01, 0xff, 0x10, 0x10, 0x10};

110 IBM Smalltalk: Programmers Reference


Reading bitmaps from files
Bitmaps can be read from files by sending the
readBitmapFile:widthReturn:heightReturn:bitmapReturn:xHotReturn:yHotReturn:
message to any CgDrawable. Parameters passed to the method include the file
name and ReturnParameter objects for the width, height, the actual bitmap, and
the x and y hot spot location. The value of any ReturnParameter object can be
accessed by sending it the value message.
| error wReturn hReturn bReturn xReturn yReturn bitmap |
error := CgWindow default
readBitmapFile: 'brick'
widthReturn: (wReturn := ReturnParameter new)
heightReturn: (hReturn := ReturnParameter new)
bitmapReturn: (bReturn := ReturnParameter new)
xHotReturn: (xReturn := ReturnParameter new)
yHotReturn: (yReturn := ReturnParameter new).
error = 0
ifTrue: [
Transcript cr; show: 'File read successful. ',
'Bitmap width: ', wReturn value printString,
', height: ', hReturn value printString,
', hotspot: ', (xReturn value @ yReturn value) printString.
bitmap := bReturn value.
bitmap freePixmap]
ifFalse: [Transcript nextPutAll: 'File read error: ',
error printString; cr].

Displaying bitmaps
To display a bitmap, the copyPlane:gc:srcX:srcY:width:height:destX:destY:plane: method
is used instead of copyArea:, which is used to copy between drawables having the
same depth. Like copyArea:, copyPlane: specifies the destination drawable, the
source rectangle, and the destination point. It has an extra argument that specifies
which plane of the source drawable to use. When displaying bitmaps, this
argument should have a value of 1.

Unlike copyArea: which copies pixel values directly between the source and
destination drawables, copyPlane: expands the monochrome source bitmap to color
before performing the copy. Each pixel is copied as follows: If the source pixel
value is 1, the foreground pixel value in the GC is combined with the destination
pixel value using the GC function, and the resulting pixel value is set in the
destination. If the source pixel value is 0, the background pixel value in the GC is
combined with the destination pixel value using the GC function, and the resulting
pixel value is set in the destination.

The following example creates a brick pattern bitmap and copies it to the root
window using black as the foreground color and white as the background color:
| gc window bits bitmap |
gc := CgGC default.
window := CgWindow default.

bits := #[
2r11111111
2r00000001
2r00000001
2r00000001
2r11111111
2r00010000
2r00010000
2r00010000].

Chapter 6. Common Graphics 111


bitmap := window
createBitmapFromData: bits
width: 8
height: 8.
gc
setForeground: CgWindow default blackPixel;
setBackground: CgWindow default whitePixel.
10 to: 50 by: 8 do: [:y |
10 to: 50 by: 8 do: [:x |
bitmap
copyPlane: window
gc: gc
srcX: 0
srcY: 0
width: 8
height: 8
destX: x
destY: y
plane: 1]].
bitmap freePixmap

Two additional methods, copyAreaMasked: and copyPlaneMasked:, use a mask to


specify transparency.

Tip: Note that Common Widgets provides a platform-independent way of


requesting and releasing handles to shared system pixmaps in a pixmap cache
using the getPixmap:foreground:background:,
getPixmap:foreground:background:background:pallette:, and destroyPixmap: methods
of CgScreen. The pixmaps are created from images in an image cache.

Common Graphics image support


An image is a two-dimensional array of pixels, with an associated palette defining
the pixel values to color mapping. An icon is a small image augmented with
tranparency information. Modern graphical user interfaces require not only the
ability to draw lines, rectangles, arcs, and text, but also to display images and
icons. The support for this in Xlib, on which the core Common Graphics classes are
based, is minimal, providing only a low-level mechanism (XImage) for transferring
image data between the application and drawables. Xlibs color mechanism
(Colormap) is also very low level. The XImage and Colormap mechanisms place a
heavy burden on the application, leaving it responsible for parsing image file
formats, allocating the images palette of colors one color at a time, remapping the
image data to use the allocated pixel values for the colors, transferring the
remapped data to a window or a pixmap whenever the image needs to be
redrawn, and freeing color resources when the image is no longer required.

Common Graphics Image Support provides higher level abstractions for handling
images, icons, colors, and palettes of colors. In keeping with the design philosophy
of IBM Smalltalk, these abstractions are implemented using the highest level
capabilities applicable on each platform. Image Support provides the following
additional capabilities:
v A color class, which allows the application to describe a color
v Palette classes, which allow the application to describe the set of colors it
requires or the set of colors associated with an image
v A device-independent representation of images, which allows the application to
create, modify, or display an image

112 IBM Smalltalk: Programmers Reference


v An icon class, which allows the application to describe an icon as a single object
containing both its image and its mask, simplifying the use of icons in labels,
buttons, and other widgets, and in operations such as drag-and-drop
v Classes for reading and writing image and icon file formats, with several
standard file formats supported

Specifying colors
To specify colors, Common Graphics defines the following class:
CgRGBColor
Specifies a color using integer values in the range 065535 giving the
intensities of the color primaries red, green, and blue.

There are three ways to create a CgRGBColor in Common Graphics as follows:


v Specify the red, green, and blue intensities of the color with integers between 0
(full off) to 65535 (full on).
v Specify a color name to look up in a database of colors.
v Specify the RGB intensities in string form.

Specifying colors as RGB intensities


To specify a color by its red, green, and blue (RGB) intensities, send the
red:green:blue: message to CgRGBColor with integer arguments specifying the
intensities of the red, green, and blue components of the color. The values range
between 0 and 65535. Higher values add more of the color to the color mix. Lower
values specify less color in the color mix. In the following example, color1 specifies
50% gray and color2 specifies bright magenta.
| color1 color2 |
color1 := CgRGBColor red: 32767 green: 32767 blue: 32767.
color2 := CgRGBColor red: 65535 green: 0 blue: 65535

The intensity: class method of CgRGBColor creates a gray-scale color, where the red,
green, and blue intensities have the same value. In the following example, color1
and color2 are equivalent:
| color1 color2 |
color1 := CgRGBColor intensity: 40000.
color2 := CgRGBColor red: 40000 green: 40000 blue: 40000.

The red, green, and blue components of a CgRGBColor can be retrieved using the
red, green, and blue methods.

Tip: The black and white class methods of CgRGBColor answer preexisting instances
of CgRGBColor representing black (all intensities = 0) and white (all intensities
= 65535), respectively.

Specifying colors by name


Common Graphics provides a selection of predefined named colors. The RGB
values of these colors are stored in an internal database and can be referenced by
name at any time by an application. A subset of these named colors is listed in the
following table:
Table 21. A subset of the predefined named colors
aquamarine black blue
blue violet brown cadet blue
coral cornflower blue cyan

Chapter 6. Common Graphics 113


Table 21. A subset of the predefined named colors (continued)
dark green dark olive green dark orchid
dark slate blue dark slate gray dark turquoise
dim gray firebrick forest green
gold gray0 gray10
gray100 gray20 gray30
gray40 gray50 gray60
gray70 gray80 gray90
green green yellow indian red
khaki light blue light steel blue
lime green magenta maroon
medium aquamarine medium blue medium forest green
medium goldenrod medium orchid medium sea green
medium slate blue medium spring green medium turquoise
medium violet red navy blue orange
orange red orchid pale green
pink plum red
salmon sandy brown sea green
sienna sky blue slate blue
spring green steel blue tan
thistle turquoise violet
violet red wheat white
yellow

The RGB values of a named color can be looked up in the color database by
sending the lookupColor: message to a CgScreen. The method is passed the color
name to look up. The result is the corresponding CgRGBColor, if the color name
was found in the database, or nil if the name was not found.
| color |
color := CgScreen default lookupColor: 'orange'.
Transcript cr; show: color printString.

This code prints a string such as aCgRGBColor(16rFFFF, 16rA5A5, 16r0000) on the


Transcript Window. The actual values can differ, depending on the platform.

Parsing a color-specification string


A color-specification string consists either of a color name or a pound character (#)
followed by a 3-, 6-, or 12-hex digit specification of the colors red, green, and blue
intensities. The following examples specify various approximations of the color
magenta: magenta, #F0F, #FF00FF, #FFFF0000FFFF. A color-specification string
can be parsed for the RGB values it specifies by sending the parseColor: message to
a CgScreen with the color-specification string as the argument. The result is the
corresponding CgRGBColor, if the string was successfully parsed, or nil if the string
could not be parsed. If the specification string is a color name, the result is the
same as using lookupColor:.
| color |
color := CgScreen default parseColor: '#300050008000'.
Transcript cr; show: color printString.

The preceding code prints the string:


aCgRGBColor(16r3000, 16r5000, 16r8000).

Using palettes
A palette defines a mapping from pixel values to colors. Palettes have two
common uses:

114 IBM Smalltalk: Programmers Reference


v Specifying a set of colors that an application requires. The palette is selected into
a shell window before any drawing occurs, to ensure that its colors are available
for later drawing requests. A shell window has exactly one palette associated
with it. This palette is called the selected palette. When drawing is done in a
window, the foreground and background values in the CgGC are used as indices
into the selected palette.
v Specifying the mapping from pixel values to colors in an image. Images are
often represented using 8 or fewer bits per pixel. In these cases, a palette defines
the colors corresponding to each pixel value in the image. Images using 16, 24,
or more bits per pixel typically encode colors directly in the pixel values. In a
24-bit-per-pixel image, one byte is used to encode each of the primary (red,
green, or blue) intensities. In these cases the palette defines which bits in a pixel
value map to each color primary.

Common Graphics defines the following palette classes:


CgPalette
Abstract superclass of CgIndexedPalette and CgDirectPalette.
CgIndexedPalette
Defines a pixel-value-to-color mapping as an array of colors, indexed by
0-based pixel values. There is no restriction on the size of the palette (sizes
of 2, 16, and 256 are often encountered in standard image formats). Colors
are undefined for pixel values not in the palette. A drawable can select a
CgIndexedPalette but not a CgDirectPalette.
CgDirectPalette
Defines a pixel-value-to-color mapping that encodes colors directly in pixel
values. The palette specifies which bits in a pixel value correspond to each
of the RGB primaries. Only the specified bits are used when doing
conversions; other bits in a pixel value are ignored. A CgDirectPalette
cannot be selected in a drawable. It is used only to specify the
pixel-value-to-color mapping of images using direct pixel values. The
following sections describe ways of selecting a CgDirectPalette, in order to
display images using direct pixel values.

The default palette


For applications with minimal color requirements, a default palette is provided
that contains 16 colors. The following table gives the pixel-value-to-color mapping
of the default palette.
Table 22. Pixel-value-to-color mapping for the default palette
Pixel value Color Pixel value Color
0 black 1 dark red
2 dark green 3 dark yellow
4 dark blue 5 dark magenta
6 dark cyan 7 light gray
8 gray 9 red
10 green 11 yellow
12 blue 13 magenta
14 cyan 15 white

Chapter 6. Common Graphics 115


All windows have the default palette selected unless the application has selected a
different palette. Use of the default palette has some key advantages:
v It minimizes color resource allocation, leaving more colors available to other
applications that require them.
v It minimizes the amount of redrawing that needs to be done when windows
change ordering and when color resource allocations are made by other
applications.

Some platforms, such as OS/2 and Windows, provide an operating system default
palette containing some or all of these colors. On these platforms, the colors in the
Common Graphics default palette use the same color intensities as the
corresponding colors in the operating system default palette in order to minimize
color resource allocation.

Tip: The default palette is retrieved by evaluating CgIndexedPalette default.

Creating indexed palettes


If the default palette does not satisfy the applications color requirements, the
application can create its own indexed palette by using the colors: class method. In
the following example, a palette with 5 different levels of gray, from black to white
is created:
| colors palette |
(colors := Array new: 5)
at: 1 put: (CgRGBColor intensity: 16r0000);
at: 2 put: (CgRGBColor intensity: 16r1FFF);
at: 3 put: (CgRGBColor intensity: 16r7FFF);
at: 4 put: (CgRGBColor intensity: 16rBFFF);
at: 5 put: (CgRGBColor intensity: 16rFFFF).
palette := CgIndexedPalette colors: colors

The following example creates a palette using named colors:


| names colors palette |
names := #('black' 'white' 'red' 'green' 'blue' 'brown' 'aquamarine' 'pink').
colors := names collect: [:name |
CgScreen default lookupColor: name].
palette := CgIndexedPalette colors: colors

From pixel values to colors and back


As described earlier, a palette defines a mapping from pixel values to colors. Pixel
values are integers between 0 and n-1, where n is the number of colors defined by
the palette.

To find the color corresponding to a given pixel value, the at: message is sent to
the palette with the pixel value as the argument. In the following example, the
palette has three colors and so defines colors for pixel values between 0 and 2
inclusive:
| colors palette |
colors := Array
with: (CgRGBColor red: 0 green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 16rFFFF blue: 16rFFFF).
palette := CgIndexedPalette colors: colors.

palette at: 0
==> aCgRGBColor(16r0000, 16r0000, 16r0000)

116 IBM Smalltalk: Programmers Reference


palette at: 1
==> aCgRGBColor(16rFFFF, 16r0000 16r0000)

palette at: 2
==> aCgRGBColor(16rFFFF, 16rFFFF, 16rFFFF)

palette at: 3
==> aCgRGBColor(16r0000, 16r0000, 16r0000)

The last expression illustrates that if the palette does not define a color for a given
pixel value, the at: method answers black, that is, a CgRGBColor with intensities of
0.

It is also possible to find the pixel value corresponding to a given color in a palette
by using the nearestPixelValue: message. This message answers the pixel value for
the color in the palette that most closely matches the given color, as the following
example illustrates. The exact algorithm used for matching colors is described in
the specification for CgPalette>>nearestPixelValue:.
| colors palette |
colors := Array
with: (CgRGBColor red: 0 green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 16rFFFF blue: 16rFFFF).
palette := CgIndexedPalette colors: colors.

palette nearestPixelValue: (CgRGBColor red: 0 green: 0 blue: 0)


==> 0
palette nearestPixelValue: (CgRGBColor red: FFFF green: 0 blue: 0)
==> 1
palette nearestPixelValue: (CgRGBColor red: FFFF green: FFFF
blue: FFFF)
==> 2

palette nearestPixelValue: (CgRGBColor red: BFFF green: 0 blue: 0)


==> 1

The nearestColor: message answers the color in the palette that most closely
matches the given color. It is equivalent to looking up the color in the palette using
at: after getting the nearest pixel value using nearestPixelValue:.
| colors palette |
colors := Array
with: (CgRGBColor red: 0 green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 0 blue: 0)
with: (CgRGBColor red: 16rFFFF green: 16rFFFF blue: 16rFFFF).
palette := CgIndexedPalette colors: colors.
palette nearestColor: (CgRGBColor red: 0 green: 0 blue: 0)
==> (CgRGBColor red: 0 green: 0 blue: 0)

palette nearestColor: (CgRGBColor red: FFFF green: 0 blue: 0)


==> (CgRGBColor red: FFFF green: 0 blue: 0)
palette nearestColor: (CgRGBColor red: FFFF green: FFFF
blue: FFFF)
==> (CgRGBColor red: FFFF green: FFFF blue: FFFF)

palette nearestColor: (CgRGBColor red: BFFF green: 0 blue: 0)


==> (CgRGBColor red: FFFF green: 0 blue: 0)

Selecting and drawing with palettes


This section describes how to select palettes and how to draw using palettes.

Chapter 6. Common Graphics 117


Selecting a palette
Before you can draw using the colors of a given palette, the palette must first be
selected in the window of the applications shell widget. This ensures that the
palettes colors are available in the video hardware when the window is active.
This is done using the setPalette: message.

If the default palette is to be used, the setPalette: message is not required, because it
is selected by default when windows are created. Remember that CgIndexedPalettes
can be selected in windows, not CgDirectPalettes.

The setPalette: message can be sent only to the window of a shell widget or to a
pixmap. It is an error to send setPalette: to other windows. The shell windows
palette is inherited by all child windows of the shell window.

Tip: The size of selected palettes should be kept as small as possible, to minimize
color resource allocation. This gives other applications a better chance of
drawing with their preferred colors.

The following example illustrates how to select a palette. This assumes that the
shell message answers the CwTopLevelShell widget of the application (see Shell
widgets on page 158 for more information on shell widgets).
| names colors color palette |
names := #('black' 'white' 'red' 'green' 'blue' 'brown' 'aquamarine' 'pink').
colors := Array new: names size.
1 to: names size do: [:i |
color := self shell screen lookupColor: (names at: i).
color isNil ifTrue: [self error: 'Color not found.'].
colors at: i put: color].
palette := CgIndexedPalette colors: colors.
self shell window setPalette: palette

Retrieving the selected palette


The selected palette can be retrieved using the getPalette message.

The getPalette message can be sent to any drawable. If getPalette is sent to a


window that is not a shell window, the palette of the shell window is answered.

Drawing with palettes


To use a color specified by the selected palette, the corresponding pixel value is set
in the graphics context foreground or background component. For example, the
following code draws a blue-filled rectangle, assuming the palette in the previous
example is selected. This code assumes that the drawable message answers the
window of a CwDrawingArea widget (which is a child of the shell widget used in
the previous example).
| gc |
gc := self drawable
createGC: None
values: CgGCValues new.
"Blue has a pixel value of 4 in the palette."
gc setForeground: 4.
self drawable
fillRectangle: gc x: 0 y: 0 width: 100 height: 50.
gc freeGC

Alternatively, the getPalette and nearestPixelValue: methods can be used to find the
appropriate pixel value.

118 IBM Smalltalk: Programmers Reference


| gc palette pixelValue |
gc := self drawable
createGC: None
values: CgGCValues new.
palette := self drawable getPalette.
pixelValue := palette
nearestPixelValue: (self shell screen lookupColor: 'blue').
gc setForeground: pixelValue.
self drawable
fillRectangle: gc x: 0 y: 0 width: 100 height: 50.
gc freeGC

Drawing in black and white


If drawing is done in black or white, the blackPixel and whitePixel methods of
CgDrawable can be used to determine the pixel value to use for the selected palette.

The blackPixel and whitePixel methods of CgDrawable answer the pixel value of the
color closest to black and white, respectively, in the drawables selected palette.

Drawing in pixmaps using palettes


Drawing in pixmaps requires special consideration when using palettes other than
the default palette.

When a CgPixmap is first created using createPixmap:, the default palette is selected
in the pixmap, not the palette of receiver of the createPixmap: message. The receiver
is only used to specify the screen and compatible depths of the pixmap. Because
the receiver might not be the intended destination of a copy operation using the
pixmap, the receivers palette is not selected in the pixmap.

The palette of the window in which the pixmap is to be displayed must be selected
in the pixmap before drawing is done. This is required so that the
pixel-value-to-color mapping of the pixmap is the same as that of the destination
window. If this is not done, the pixel-value-to-color mappings for the pixmap and
the destination window will be different, and the results will not be what is
expected when the pixmap is copied to the window.

The following code shows the correct way to draw in a pixmap using a palette
other than the default palette. The code assumes that the shell method answers the
CwTopLevelShell widget and that the drawingArea method answers the
CwDrawingArea widget in which the pixmap will be displayed.
| shellWindow destWindow colors color palette pixmap gc |
shellWindow := self shell window.
destWindow := self drawingArea window.
"Create the palette."
colors := Array new: 16.
0 to: 15 do: [:i |
color := CgRGBColor red: 65535 * i // 15 green: 0 blue: 0.
colors at: i + 1 put: color].
palette := CgIndexedPalette colors: colors.
"Select the palette in the shell window."
shellWindow setPalette: palette.

"Create the pixmap and select the palette in it."


pixmap := destWindow
createPixmap: 320 height: 50 depth: destWindow depth.
pixmap setPalette: palette.
"Draw vertical bars in increasing intensities of red in the pixmap."
gc := pixmap
createGC: None
values: nil.

Chapter 6. Common Graphics 119


0 to: 15 do: [:i |
gc setForeground: i.
pixmap
fillRectangle: gc x: i * 20 y: 0 width: 20 height: 50].
"Copy the pixmap to the destination window."
pixmap
copyArea: destWindow gc: gc srcX: 0 srcY: 0 width: 320 height: 50
destX: 0 destY: 0.
"Free the gc and pixmap."
gc freeGC.
pixmap freePixmap

Direct palettes
Direct palettes, represented by the class CgDirectPalette, specify a
pixel-value-to-color mapping for pixel values that directly encode colors. They are
used primarily in bitmapped images with 16 or more bits per pixel. A direct palette
specifies which bits of a pixel value correspond to each of the color primaries. This
correspondence is defined by three nonoverlapping bit masks.

For example, an image with 24 bits per pixel directly encodes each pixels color in
the corresponding pixel value with 8 bits for each of the primary color (red, green,
and blue). The following code creates a CgDirectPalette where the low-order byte of
a 24-bit pixel value gives the intensity of red, the next highest byte gives the
intensity of green, and the high byte gives the intensity of blue.
| palette |
palette := CgDirectPalette
redMask: 16rFF
greenMask: 16rFF00
blueMask: 16rFF0000

The at:, nearestPixelValue:, and nearestColor: methods described for indexed palettes
are also applicable to CgDirectPalettes. The following example illustrates the use of
at: on the palette created above:
palette at: 16r000000
==> aCgRGBColor(16r0000, 16r0000, 16r0000)
palette at: 16rFFFFFF
==> aCgRGBColor(16rFFFF, 16rFFFF, 16rFFFF)
palette at: 16r1F3F7F
==> aCgRGBColor(16r7F7F, 16r3F3F, 16r1F1F)

Note: The pixel values in this case use 8 bits (0255) each for red, green, and blue,
whereas CgRGBColors use 16 bits (065535). The red component of the pixel
value in the last example is 16r7F. To convert this to 16 bits it is multiplied
by 65535 and divided by 255 giving 16r7F7F.

Device-independent images
Conceptually, an image is a two-dimensional array of colors. For storage efficiency,
colors in an image are usually encoded as discrete pixel values with a fixed
number of bits for each pixel value. There are two common ways in which colors
are encoded as pixel values. In the first kind of encoding, the image is restricted to
a small set of colors (256 or less). This set is ordered and the pixel values are
treated as indices into the list of colors. This encoding requires 8 bits or fewer per
pixel. In the second kind of encoding, colors are encoded directly in the pixel
values. For example, the red, green, and blue intensities of a color can be encoded
as values in the range 0 to 255, requiring 8 bits for each primary and therefore 24
bits for the whole pixel value. For both encodings a palette is required to define
the pixel-value-to-color mapping.

120 IBM Smalltalk: Programmers Reference


Common Graphics provides a class, CgDeviceIndependentImage, that defines a
device-independent representation of uncompressed bitmap images with 1, 4, 8, or
24 bits per pixel. The image data format is made public, allowing it to be
efficiently manipulated by applications. The same data format is used on all
platforms. The data format is as follows:
v Scanlines are ordered from top (scanline 0) to bottom (scanline height-1).
v The number of bytes representing a scanline is rounded up to an integral
multiple specified on image creation (usually 1, 2, or 4 bytes).
v In images of depth 1 or 4, the pixels are ordered in each byte from left to right
starting at the most significant bit of the byte.
v In images of depth greater than 8, the bytes comprising the pixel value are
ordered with the most significant byte first.

Device-independent images are created in one of two ways:


v Created by the application using application-defined data
v Loaded from a file

The following section describes how to create an image using application-defined


data. Image file formats are described in a later section.

Creating and manipulating images


To create a blank image, use the width:height:depth:palette: class method of
CgDeviceIndependentImage. The image data is initialized to all zeros. The following
example shows how to create a 100-by-50 pixel gray-scale image with 8 bits per
pixel. Because the palette specifies black for pixel value 0, the image is initially all
black.
| colors color palette image line gc |
colors := Array new: 256.
0 to: 255 do: [:i |
color := CgRGBColor intensity: i * 65535 // 255.
colors at: i + 1 put: color].
palette := CgIndexedPalette colors: colors.
image := CgDeviceIndependentImage
width: 100
height: 50
depth: 8
palette: palette.

The image attributes can be retrieved using the width, height, depth, palette, and data
methods of CgDeviceIndependentImage. The extent method answers the width and
height as a point.

To modify a single pixel value, use the putPixel:y:pixelValue: method. This method
takes the x- and y- coordinates of the pixel and stores the new pixel value at that
location in the image.

Tip: As with the drawing methods described in previous sections, the x-and
y-coordinates within images start at 0. The point 0@0 is at the top left corner
of the image, with the x-coordinate increasing from left to right, and the
y-coordinate increasing from top to bottom.

Continuing the previous example, the following code modifies the image data one
pixel at a time. The resulting image has adjacent vertical stripes increasing in
intensity from black at the left to white at the right.

Chapter 6. Common Graphics 121


0 to: 99 do: [:x |
intensity := x * 255 // 99.
0 to: 49 do: [:y |
image putPixel: x y: y pixelValue: intensity]].

A more efficient way of modifying image data is to use the


putPixels:y:width:pixels:startIndex: method, which modifies a horizontal line of pixels
in a given scanline. The following code modifies the image data with the same
result as the previous code, but is more efficient.
line := ByteArray new: 100.
0 to: 99 do: [:x |
line at: x + 1 put: x * 255 // 99].
0 to: 49 do: [:y |
image putPixels: 0 y: y width: 100 pixels: line startIndex: 1].

Image data can be retrieved one pixel at a time using the getPixel:y: method. A
horizontal line of pixels can be retrieved using getPixels:y:width:pixels:startIndex:. A
new image can be copied from an area of an existing image using the getSubImage:
method and passing it a rectangle. The getPixel:... and putPixel: methods have
getColor: and putColor: equivalents that answer and accept CgRGBColor objects
rather than pixel values and use the images palette to convert between colors and
pixel values.

Displaying images
The putDeviceIndependentImage:image:srcRect:destRect: method of CgDrawable is used
to display images. As with all drawing operations, the first argument is a CgGC
specifying the drawing attributes. The next argument is the image to display. The
last two arguments are a source rectangle, specifying the area of the image to
display, and a destination rectangle, specifying the area of the drawable in which
to display the source area. If the source and destination rectangles do not have the
same extent, the source area of the image is stretched or shrunk to fit in the
destination area. The image can be flipped by specifying a negative width or
height in the destination or source rectangles.

The following code displays the image created in the previous example, stretched
by a factor of two. To ensure that the required colors are available in the video
hardware, the images palette is first selected in the window of the shell widget.
This code assumes that the shell method answers the shell widget and that the
drawable method answers the window of the drawing area.

self shell window setPalette: image palette.


gc := self drawable +
createGC: None
values: CgGCValues new.
self drawable
+
putDeviceIndependentImage: gc
image: image
srcRect: (0@0 extent: image extent)
destRect: (20@20 extent: image extent * 2).
gc freeGC

Direct color images


A direct-color image is an image in which the pixel values directly encode the
colors. Typically these images have 16 or more bits per pixel, with 24 bits per pixel
being the most common. A CgDirectPalette is used to define the pixel-value-to-color
mapping for direct-color images. Although CgDirectPalettes cannot be selected into
a CgDrawable, a CgIndexedPalette creation convenience method, colorCube:, can be

122 IBM Smalltalk: Programmers Reference


used to create an approximation of all possible colors. The colorCube: method takes
an array of three integers specifying the number of levels of red (R), green (G), and
blue (B) to include in the palette. The resulting palette has R*G*B entries. The
following example creates and displays a 256-by-256 direct-color image with a
constant amount of red, with green increasing from top to bottom, and with blue
increasing from left to right. The resulting image has red at the top left corner,
magenta at the top right, yellow at the bottom left, and white at the bottom right.
The image is not shown here because it cannot be adequately reproduced in black
and white.
| directPalette image pixel gc r g b |
directPalette := CgDirectPalette
redMask: 16rFF
greenMask: 16rFF00
blueMask: 16rFF0000.
image := CgDeviceIndependentImage
width: 64
height: 64
depth: 24
palette: directPalette.
r := 255.
0 to: 63 do: [:y |
g := y * 255 // 63.
0 to: 63 do: [:x |
b := x * 255 // 63.
pixel := (b bitShift: 16) + (g bitShift: 8) + r.
image putPixel: x y: y pixelValue: pixel]].
self shell window setPalette:
(CgIndexedPalette colorCube: #(6 6 6)).
gc := self drawable
createGC: None
values: CgGCValues new.
self drawable
putDeviceIndependentImage: gc
image: image
srcRect: (0@0 extent: image extent)
destRect: (20@20 extent: image extent).
gc freeGC

Tip: The putDeviceIndependentImage: method supports 1-, 4-, and 8-bit per pixel
images on all platforms. Twenty-four-bit images are supported on Windows
and OS/2 PM only. 16- and 32-bit images are not supported. In the 24-bit
case, the images palette must be a CgDirectPalette with the same masks as
used in the example above. Note also that the display of 24-bit images on
Windows is very slow unless it is directly supported by the video driver.

Copying images from a drawable


A rectangular area of a drawable can be retrieved as an image using the
getDeviceIndependentImage: method of CgDrawable. This is primarily useful for
taking snapshots of the screen or specific windows. It can also be used, with
putDeviceIndependentImage:, to stretch an area of a drawable.

The following example gets the contents of the entire default screen as an image:
| screen rect image |
screen := CgScreen default.
rect := 0 @ 0 extent: screen width @ screen height.
image := screen rootWindow getDeviceIndependentImage: rect

Chapter 6. Common Graphics 123


Icons
Icons are small (typically not more than 32-by-32 pixels) images augmented with a
transparency mask. They are often used in user interface (UI) components such as
buttons and labels, as well as in UI operations such as drag-and-drop. The class
CgIcon represents an icon in Common Graphics. CgIcon objects can be created
using application-defined data, by loading from a dynamic link library (DLL), by
specifying a system icon constant, or by specifying an operating system (OS) icon
representation.

Creating icons
The fromImage:maskImage: class method creates a CgIcon given a shape image and a
mask image. Wherever there is a 1 in the mask image, the icon is opaque and the
corresponding pixel in the shape image is displayed. Wherever there is a 0 in the
mask image, the icon is transparent and the corresponding pixel in the shape
image is ignored. Specifying nil as the mask image is equivalent to specifying a
mask image of all 1s (entirely opaque).

Tip: The palette of the mask image arugment of fromImage:maskImage: is ignored


when creating an icon. Transparency information is specified directly by the
mask data, with 1 indicating opaque and 0 indicating transparent.
An icon must be freed using the freeIcon method when it is no longer required, in
other words, when it will no longer be drawn and it is not set as the value of an
icon resource of any widget.

The following example creates an 8-by-8 monochrome icon of a white cross with a
black border. The icon is transparent outside of the white border. The icon is not
freed because it will be used in the next example.
| palette shapeImage maskImage icon |
palette := CgIndexedPalette colors:
(Array
with: CgRGBColor black "0s are black"
with: CgRGBColor white). "1s are white"
shapeImage := CgDeviceIndependentImage
width: 8
height: 8
depth: 1
palette: palette
scanlinePad: 1
data: #[
2r00111100
2r00100100
2r11100111
2r10000001
2r10000001
2r11100111
2r00100100
2r00111100].
maskImage := CgDeviceIndependentImage
width: 8
height: 8
depth: 1
palette: palette "The mask image palette is not used."
scanlinePad: 1
data: #[
2r00111100
2r00111100
2r11111111
2r11111111

124 IBM Smalltalk: Programmers Reference


2r11111111
2r11111111
2r00111100
2r00111100].
icon := CgIcon
fromImage: shapeImage
maskImage: maskImage

Drawing icons
The drawIcon:x:y:icon: method of CgDrawable is used to draw CgIcon objects. The
following code draws the icon, created in the previous example, at the top left
corner of the screen. The icon is freed because it is no longer required.

CgWindow default
drawIcon: CgGC default
x: 0
y: 0
icon: icon.
icon freeIcon

As with images, CgIcons have a platform- and device-independent data


representation. However, for speed, CgIcons are displayed using the operating
systems representation of icons. On OS/2 PM the limit is 32x32 or 40x40,
depending on the video driver. On Windows, the size of operating-system icons is
limited to 32x32. If an icon is created larger than the maximum size on these
platforms, it is clipped to the maximum size and only its top left corner is drawn.

The best icon size to use can be obtained by sending queryBestIconSize to the
CgScreen. On Window and OS/2 PM this size is the maximum displayable icon
size. On X this size is only a suggestion, not a hard limit.

CgIcons are displayed using only the default palette colors. It is not necessary to
select a palette in the destination drawable. The drawIcon: method ignores the
selected palette.

Loading icons from DLLs


On Windows and OS/2 platforms, CgIcons can be loaded from an icon resource
linked into the virtual machine (VM) executable or another file. A platform-specific
resource specifier object is used to identify the icon resource.

On OS/2, only integers are used as resource specifiers. On Windows, resource


specifiers can be either strings (as in the previous example) or integers.

The following code illustrates how to load an icon resource from the VM
executable on the Windows platform. Do this using the fromResource: class method:
| icon |
icon := CgIcon fromResource: 'APP_ICON'.
CgWindow default
drawIcon: CgGC default x: 0 y: 0 icon: icon.
icon freeIcon

The equivalent code for OS/2 is as follows:

Chapter 6. Common Graphics 125


| icon |
icon := CgIcon fromResource: 1.
CgWindow default
drawIcon: CgGC default x: 0 y: 0 icon: icon.
icon freeIcon

To load an icon resource from a file other than the VM executable, use the
fromResource:fileName: class method of CgIcon. The following examples load an icon
from the VM executable, as above, but the file name is explicitly specified:
"Windows"
| icon |
icon := CgIcon
fromResource: 'APP_ICON' fileName: 'abt.exe'.
CgWindow default
drawIcon: CgGC default x: 0 y: 0 icon: icon.
icon freeIcon
"OS/2"
| icon |
icon := CgIcon
fromResource: 1 fileName: 'abt.exe'.
CgWindow default
drawIcon: CgGC default x: 0 y: 0 icon: icon.
icon freeIcon

Using operating system icons


Common Graphics provides two methods for creating a CgIcon given an operating
system icon representation. The fromOSIcon: class method creates a CgIcon given an
operating system icon. The fromSystem: class method is similar, but the operating
system icon is specified as a platform-specific system icon constant.

On OS/2 PM, operating system icons are represented by class OSHpointer. On


Windows, operating system icons are represented by class OSHicon.

Common Widgets provides a platform-independent way of requesting and


releasing handles to share system icons in an icon cache using the
getIcon:foregroundColor: and destroyIcon: methods of CgScreen.

Image and icon file formats


There are many file formats used for storing images and icons. Common Graphics
provides several classes for reading and writing standard file formats. The
following image file formats are supported:
Table 23. Supported image file formats
Format Description
Windows BMP Native format for the Windows platform
PM BMP Native format for the PM platform
TIFF Portable format, supported by many tools on different platforms
PCX Common file format on PC platforms

The following icon file formats are supported:


Table 24. Supported icon file formats
Format Description
Windows ICO Native format for the Windows platform

126 IBM Smalltalk: Programmers Reference


Table 24. Supported icon file formats (continued)
Format Description
PM ICO Native format for the PM platform

The following table shows the class hierarchy that implements the file format
framework of Common Graphics. This framework provides methods to load and
unload images and icons in these formats, to test whether a file has a given format,
and to detect what the format of a given file is.
Table 25. File format class hierarchy
Class Description.
CgFileFormat Abstract file format class
CgIconFileFormat Abstract icon file format class
CgPMICOFileFormat PM ICO file format
CgWinICOFileFormat Windows ICO file format
CgImageFileFormat Abstract image file format class
CgPCXFileFormat PCX file format
CgPMBMPFileFormat PM BMP file format
CgTIFFFileFormat TIFF file format
CgWinBMPFileFormat Windows BMP file format

In addition to loading from files and unloading into files, Common Graphics
provides methods to load from ByteArrays and unload into ByteArrays. Error
handling methods are also provided. These methods are described in the following
sections.

Loading images from files


To load an image from a file, the loadFromFileHandle:atOffset: message is sent to an
instance of the appropriate subclass of CgImageFileFormat. The first argument is a
CfsFileDescriptor, opened for reading on the input file. The atOffset: argument is the
number of bytes from the start of the file at which to start reading. The result is a
CgDeviceIndependentImage if the file was successfully loaded, or nil if an error
occurred.

The following example illustrates how to load a PCX image from a file named
my-image.pcx:
| format file image |
format := CgPCXFileFormat new.
file := CfsFileDescriptor open: 'my-image.pcx' oflag: ORDONLY.
image := format loadFromFileHandle: file atOffset: 0.
file close.
|image

The loadFromFile: method takes a file name directly, saving the application from
having to explicitly open and close the file. The following code has the same result
as the previous example:
| format image |
format := CgPCXFileFormat new.
image := format loadFromFile: 'my-image.pcx'.
|image

Handling errors
If an error occurs during a load, the loadFromFileHandle:atOffset: method answers
nil. Another way to detect an error is to send the hasErrorOccurred message to the

Chapter 6. Common Graphics 127


format object (the instance of the CgFileFormat subclass). This method answers true
if an error occurred in the last operation, or false if no error occurred. To determine
what the error was, the currentError and currentErrorString messages can be sent to
the format object. The currentError method answers an integer error code. The
currentErrorString method answers a String describing the error. The following table
lists the error codes and corresponding error strings.
Table 26. CgFileFormat error codes and error strings
Error code Error string
1 No errors
2 Error opening file
3 Invalid file handle
4 Invalid offset
5 Invalid array of ByteArrays
6 Invalid array of integer offsets into ByteArrays
7 Seek error
8 Write error
9 Read error
10 Read-only object encountered (attempt to unload into ByteArray
marked as read-only)
11 Memory allocation error
12 File format not supported
13 File is not of specified format
14 Compression type not supported
15 Unsupported aspect of the specified format encountered
16 Invalid extra info

The following example adds error handling to the previous example of loading a
PCX file:
| format image |
format := CgPCXFileFormat new.
image := format
loadFromFile: 'my-image.pcx'.
format hasErrorOccurred
ifTrue: [self error: format currentErrorString]
ifFalse: [|image]

Loading icons from files


To load icons from a file, send loadFromFileHandle:atOffset: or loadFromFile: to an
instance of the appropriate subclass of CgIconFileFormat. Because the icon file
formats allow more than one icon to be stored in a file, these methods answer an
array of CgIcon objects if the file was successfully loaded, rather than a single
CgIcon object. As with the image file formats, nil is answered if an error occurred.
The following example illustrates how to load the icons from a Windows ICO file
named my-icons.ico. Only the first icon is answered.
| format icons |
format := CgWinICOFileFormat new.
icons := format

128 IBM Smalltalk: Programmers Reference


loadFromFile: 'my-icons.ico'.
format hasErrorOccurred
ifTrue: [self error: format currentErrorString].
|icons first

Unloading images into files


To unload an image into a file, the unload:intoFileHandle:atOffset: message is sent to
the appropriate CgImageFileFormat object. The method answers true if the image
was successfully unloaded, or false if an error occurred. As with the load methods,
the hasErrorOccurred, currentError, and currentErrorString methods can be used to
handle errors.

Note: Unloading does not modify the original object.

The following code unloads an image captured from the screen into a file named
img-out.pcx using the PCX format:
| screen rect image file format success |
screen := CgScreen default.
rect := 0 @ 0 extent: screen width @ screen height.
image := screen rootWindow
getDeviceIndependentImage: rect.
format := CgPCXFileFormat new.
file := CfsFileDescriptor
open: 'img-out.pcx'
oflag: OCREAT | OTRUNC | OWRONLY.
success := format
unload: image
intoFileHandle: file atOffset: 0.
file close.
success
ifFalse: [self error: 'Error unloading image']

The unload:intoFile: method takes a file name as an argument. It creates the file if it
does not exist, or overwrites the file if it does exist. The following code has the
same effect as the previous example.
| screen rect image format |
screen := CgScreen default.
rect := 0 @ 0 extent: screen width @ screen height.
image := screen rootWindow
getDeviceIndependentImage: rect.
format := CgPCXFileFormat new.
(format unload: image intoFile: 'img-out.pcx')
ifFalse: [self error: 'Error unloading image']

Unloading icons into files


CgIcon objects are unloaded into files using the same methods as for images.
However, as with the icon load methods, an array of CgIcons is given, rather than a
single CgIcon.

The following code unloads a single icon into a file in the Windows ICO format. It
assumes that the iconToUnload method answers the CgIcon to unload.
| icons format |
icons := Array with: self iconToUnload.
format := CgWinICOFileFormat new.
(format unload: icons intoFile: 'icon-out.ico')
ifFalse: [self error: 'Error unloading icon']

Chapter 6. Common Graphics 129


Unloading images and icons into ByteArrays
In addition to using files, Common Graphics allows images and icons to be loaded
and unloaded using ByteArrays. The totalSizeBeforeUnload: method determines the
number of bytes required to unload the image. The
unload:intoByteObjects:offsetsIntoByteObjects: method, which does the actual
unloading, takes an array of ByteArrays and an array of Integer offsets. The offsets
specify how many bytes to skip at the beginning of each ByteArray. The offsets can
be used, for example, to leave room for database record headers. As with
unload:intoFile:, true is answered if the unload was successful, false if an error
occurred.

The following code illustrates how to unload an image into an array of ByteArrays
using the PCX format. Each ByteArray is limited to a maximum size of 64K. To
illustrate how space is reserved for a database record header, 32 bytes are skipped
at the beginning of each ByteArray.
| image format headerSize maxSize imageSize numByteArrays byteArrays offsets |
image := self imageToUnload.
format := CgPCXFileFormat new.
headerSize := 32.
maxSize := 65536. "64K"
imageSize := format totalSizeBeforeUnload: image.
"Determine total size."
numByteArrays := imageSize // (maxSize - headerSize) + 1.
byteArrays := OrderedCollection new.
1 to: numByteArrays do: [:i|
byteArrays add: (ByteArray new: maxSize)].
byteArrays := byteArrays asArray.
offsets := Array new: byteArrays size.
offsets atAllPut: headerSize.
(format
unload: image
intoByteObjects: byteArrays
offsetsIntoByteObjects: offsets)
ifFalse: [self error: format currentErrorString]

Icons are unloaded into ByteArrays in a similar fashion. The only difference is that
the totalSizeBeforeUnload: and unload:intoByteObjects:offsetsIntoByteObjects: methods
for the icon file formats take an array of CgIcons rather than an image.

Loading images and icons from ByteArrays


To load images or icons from ByteArrays, use the loadFromByteObjects:-
offsetsIntoByteObjects: method.

The following code illustrates how to load a PCX format image from an array of
ByteArrays. It assumes that the imageStorage method answers the array of
ByteArrays created in the previous example.
| format headerSize byteArrays offsets image |
format := CgPCXFileFormat new.
headerSize := 32.
byteArrays := self imageStorage.
offsets := Array new: byteArrays size.
offsets atAllPut: headerSize.
image := format
loadFromByteObjects: byteArrays
offsetsIntoByteObjects: offsets.
image isNil
ifTrue: [self error: format currentErrorString]

130 IBM Smalltalk: Programmers Reference


Icons are loaded from ByteArrays in a similar fashion. As with loadFromFile:, the
loadFromByteObjects:offsetsIntoByteObjects: method for icon file formats answers an
array of CgIcons.

Determining the format of a file


Common Graphics provides methods to do the following:
v Determine whether a file has a given file format
v Determine which format a given file has

The formatMatchesFileHandle:atOffset: class method answers true if the given file


matches the receivers format, false if it does not. For example, the following code
tests whether the file named my-image.pcx is actually a PCX image file:
| file |
file := CfsFileDescriptor open: 'my-image.pcx' oflag: ORDONLY.
(CgPCXFileFormat formatMatchesFileHandle: file atOffset: 0)
ifTrue: [Transcript cr; show: 'Format matches.']
ifFalse: [Transcript cr; show: 'Format does not match.'].
file close

The formatMatchingFileHandle:atOffset:ifNone: class method determines which format


matches the given file. It answers the appropriate file format class if a matching
format was found. If one was not found, the block passed as the last argument is
evaluated and its result is answered. The following code determines the format of
the file named my-image.pcx:
| file formatClass |
file := CfsFileDescriptor open: 'my-image.pcx' oflag: ORDONLY.
formatClass := CgImageFileFormat
formatMatchingFileHandle: file
atOffset: 0
ifNone: [file close. |self error: 'No matching format found'].
file close.
Transcript cr; show: 'Matching format: ', formatClass printString

When the input is an array of ByteArrays, use the formatMatchesByteObjects:-


offsetsIntoByteObjects: and formatMatchingByteObjects:offsetsIntoByteObjects:- ifNone:
class methods.

Extra file format information


Image file formats often specify more information than is represented by
CgDeviceIndependentImage. For example, image data in BMP format can either be
uncompressed or compressed using run-length encoding. The application might
need to know the data compression technique used by a loaded images file so that
the image can later be unloaded in the same way. The image file format objects
preserve this extra information as part of their state when an image is loaded, and
use this information when an image is unloaded.

Each of the CgFileFormat classes provides format-specific methods to access and


change the extra information. These methods are outlined in the following sections.

Following these sections, an example illustrates how to manipulate extra


information.

PCX
The following methods provide extra information preserved and used by the
CgPCXFileFormat class:
hRes Horizontal resolution of device

Chapter 6. Common Graphics 131


paletteInfo
How to interpret palette (color or gray scale)
version Format version number
vRes Vertical resolution of device

PM BMP
The following methods provide extra information preserved and used by the
CgPMBMPFileFormat class:
colorEncode
Color encoding scheme
colorsUsed
Number of colors used by the image
compression
Data compression algorithm used
ident Application specific identifier
importantColors
Number of colors important for displaying image
recording
Recording algorithm used
rendering
Halftoning algorithm used
resUnits
Units of measure for xRes and yRes
size1 Halftoning correction factor
size2 Halftoning correction factor
xRes Horizontal resolution of device
yRes Vertical resolution of device

TIFF
The following methods provide extra information preserved and used by the
CgTIFFFileFormat class:
colorScheme
Color scheme (gray scale, palette, RGB)
compression
Data compression algorithm used
predictor
Prediction method, used with some types of compression

Windows BMP
The following methods provide extra information preserved and used by the
CgWinBMPFileFormat class:
compression
Data compression algorithm used
importantColors
Number of colors important for displaying image
pelsPerMeter
Horizontal and vertical resolution of device

Example
The following code illustrates how to unload an image in Windows BMP format,
using run-length encoding of the data if the image has a depth of 4 or 8. The
device resolution and number of important colors are also specified.
| image format |
image := self imageToUnload.
format := CgWinBMPFileFormat new.
format compression: 0. "No compression by default."
image depth = 8
ifTrue: [format compression: 1]. "RLE8 compression."

132 IBM Smalltalk: Programmers Reference


image depth = 4
ifTrue: [format compression: 2]. "RLE4 compression."
format importantColors: (1 bitShift: image depth) - 3.
"All but 3 colors are important. Approx. 75 dots per inch."
format pelsPerMeter: 3000 @ 3000.
format unload: image intoFile: 'img-out.bmp'

Resource management summary


Most Common Graphics resources need to be explicitly deallocated when no
longer required, otherwise operating system resources are lost and will eventually
run out. This includes fonts, pixmaps, color cells, graphics contexts, cursors, and
icons.

Often the best place to deallocate graphics resources is in a destroy callback


handler for the drawing area widget in which graphics are being drawn. See
Drawing area widgets on page 164 for more information.

The following table summarizes when and how to free graphics objects allocated
by the application:
Table 27. Graphics object freeing reference
Graphics object Summary
CgCursor Obtained using: CgDisplay>>createFontCursor
CgPixmap>>createPixmapCursor
CgFont>>createGlyphCursor
Freed using: CgCursor>>freeCursor
When to free: When it will no longer be set as the cursor for
any window (using
CgWindow>>setWindowCursor:). Cursors are
normally allocated when an application is
opened and freed when it is closed.
CgDeviceIndepen- Obtained using: CgDeviceIndependentImage class>>
dentImage width:height:depth:palette:
width:height:depth:palette:scanlinePad:data:
Freed using: <None>
When to free: Images do not need to be explicitly freed.
CgDirectPalette Obtained using: CgDirectPalette class>>
redMask:greenMask:blueMask:
Freed using: <None>
When to free: Direct palettes do not need to be explicitly
freed.
CgGC Obtained using: CgWindow>>createGC:
Freed using: CgGC>>freeGC
When to free: When it is no longer required for graphics
operations on the drawable for which it was
created. It is typically freed when a window is
closed by hooking the destroy callback.

Chapter 6. Common Graphics 133


Table 27. Graphics object freeing reference (continued)
Graphics object Summary
CgFont Obtained using: CgDisplay>>loadFont:
Freed using: CgFont>>unloadFont
When to free: When it is no longer required for graphics
operations on any drawable. It is typically freed
when no more text will be drawn with that font.
The font can remain selected in a CgGC but if a
request using the font is made, such as drawing
a string, the results are undefined.
CgFontStruct Obtained using: CgDisplay>>loadQueryFont:
CgFont>>queryFont
Freed using: CgFontStruct>>freeFont
When to free: When it is no longer required for information
about its font and when no more text will be
drawn with that font. The same conditions as
for freeing CgFont apply.
CgIndexedPalette Obtained using: CgIndexedPalette class>>
entries:
colors:
Freed using: <None>
When to free: Indexed palettes do not need to be explicitly
freed.
CgIcon Obtained using: CgIcon class>>
fromImage:maskImage:
fromOSIcon:
fromResource:
fromResource:fileName:
fromResources:
fromResources:fileName:
fromSystem:
width:height:depth:palette:shapePad:-
shapeData:maskPad:maskData:
Freed using: CgIcon>>freeIcon
When to free: When it will no longer be drawn and it is no
longer set as an icon resource of a widget.
CgPixmap Obtained using: CgDrawable>>
createPixmap:
createBitmapFromData:
createPixmapFromBitmapData:
readBitmapFile:
Freed using: CgPixmap>>freePixmap
When to free: When it will no longer be used for drawing on,
it will no longer be used as the source for a
copy operation, and it will no longer be used as
a tile or stipple pattern in a GC.
CgArc, CgCharStruct, Obtained using: <Misc>
CgFontProp,
CgGCValues, Freed using: <None>
CgRGBColor,
CgSegment, When to free: These objects are informational data structures
CgTextItem only, and need not be explicitly freed.

134 IBM Smalltalk: Programmers Reference


Chapter 7. Common Widgets
The Common Widgets (CW) classes and methods subsystem enables developers to
design and build graphical user interfaces using an application programming
interface (API) based on OSF/Motif. Using the Common Widgets subsystem,
developers can do the following:
v Create individual widgets, including buttons, lists, text, menus, and dialog boxes
v Create compound widget structures by combining individual widgets
v Specify the positioning of widgets relative to each other
v Program operations to occur in response to user actions

These capabilities are described later in this chapter. In addition, this chapter
explains how the system is based on OSF/Motif, gives an overview of the
Common Widgets class hierarchy, and describes the basic approach for building an
application.

OSF/Motif compatibility
The Common Widgets subsystem is based on the OSF/Motif C programming
interface standard. This section is of interest to developers familiar with Motif. It
describes the strategy used to translate Motif C types and functions to Smalltalk
classes and methods. Experienced Motif developers will be able to apply their
knowledge of the C programming interface directly when programming Common
Widgets.

Smalltalk classes have been created for most C data types. These classes are named
by prefixing the Motif data structure name with Cw (after first removing any X,
Xt, or Xm prefix). For example, the Motif data structure Widget is represented by
the Smalltalk class CwWidget.

Motif functions have corresponding Smalltalk methods. To understand this


mapping, consider the following function XmListSelectItem:
void XmListSelectItem (widget, item, notify)
Widget widget;
XmString item;
Boolean notify;

In the Common Widgets subsystem, the XmListSelectItem call has been mapped to
an instance method of the class CwList:
selectItem: item notify: notify

The C type Widget, in this case, is mapped to the Smalltalk class CwList because
the XmListSelectItem function applies only to list widgets. The XmList prefix has
been stripped off, because such C-specific prefixing is unnecessary in Smalltalk.

Where C types have appropriate corresponding Smalltalk base classes, C types are
mapped to these. For example, the C type XmString is mapped to the Smalltalk
class String, and the C type Boolean is mapped to the Smalltalk class Boolean.

Common Widgets class hierarchy


This section describes the Common Widgets class hierarchy and provides an
overview of the functionality provided by each class.

Copyright IBM Corp. 1994, 2000 135


A is a user interface component, such as a top-level window (shell), button or list.
A graphical user interface is built by creating a tree of widgets. Every widget
except the topmost widget in a tree has a parent widget. In the user interface a
child widget appears on top of the parent and is normally prevented from
drawing outside the bounds of its parent. Each parent widget is responsible for
sizing and positioning its children. The parent-child relationship defines the widget
tree. The topmost widget in the tree is called a shell widget. Shell widgets are
responsible for negotiating with the window manager for their position on the
screen, and for the various window decorations that they display, such as a title,
border, or close box.

All Common Widgets are subclasses of one of the following classes:


CwPrimitive
A primitive widget has no children.
CwComposite
A composite widget can have zero or more children.
CwShell
A shell widget has exactly one child.

Primitive widgets are the simplest building blocks in Common Widgets. A


primitive widget is always the child of another widget. The following table
provides brief descriptions of the CwPrimitive class and its subclasses. Classes in
italics are abstract classes (they are never instantiated).

Note: The parent-child relationship is not the same as the class-subclass


relationship.
Table 28. Primitive hierarchy
Class hierarchy Responsibility
CwWidget Defines common behavior for all widgets
CwBasicWidget Defines common behavior for widgets implemented
directly by the platform
CwPrimitive Defines common behavior for widgets that do not have
children
CwArrowButton Displays an arrow button
CwComboBox Combines a list and text area to provide a prompted entry
field
CwLabel Displays a static label that can be a string, pixmap, or
icon
CwCascadeButton Used as part of a menu system to trigger submenus
CwDrawnButton Displays a button drawn by the application
CwPushButton Displays a push button containing a string, pixmap, or
icon
CwToggleButton Displays a button that has on and off state such as a radio
or check button
CwList Displays a list of strings from which one or more can be
selected
CwScrollBar Displays a vertical or horizontal scroll bar
CwSeparator Displays a separator line, normally between menu items
CwText Displays and provides editing capability for text

Composite widgets can have zero or more child widgets. A composite widgets
children can include other composite widgets, primitive widgets, or both. Different
composite widgets provide various kinds of layout capabilities for arranging
children. The following table briefly explains the CwComposite class and its
subclasses. Classes in italics are abstract classes.

136 IBM Smalltalk: Programmers Reference


Table 29. Composite hierarchy
Class hierarchy Responsibility
CwWidget Defines common behavior for all widgets
CwBasicWidget Defines common behavior for widgets implemented
directly by the platform
CwComposite Defines common behavior for widgets that contain child
widgets
CwBulletinBoard Allows application-defined child widget positioning
CwCompositeBox Defines common behavior for CwMessageBox and
CwSelectionBox
CwMessageBox Provides a notification message, icon, and OK, Cancel,
and Help buttons
CwSelectionBox Provides a list of items to select, a text box showing the
selection, and OK, Cancel, Apply, and Help buttons
CwForm Provides a constraint-based mechanism for laying out
child widgets
CwDrawingArea Performs no child layout and provides an area for
performing graphic operations
CwFrame Provides an optionally labeled frame around its single
child widget
CwRowColumn Provides a mechanism for laying out child widgets in
rows or columns
CwScale Displays a numeric scale with a position indicator
CwScrolledWindow Allows a child widget to be scrolled using scrollbars
CwMainWindow Provides layout management for a menu bar and
optionally scrollable work area

Shell widgets provide the protocol between the application interface and the
window manager. The following table provides brief descriptions of the CwShell
class and its subclasses. Classes in italics are abstract classes.
Table 30. Shell hierarchy
Class hierarchy Responsibility
CwWidget Defines common behavior for all widgets
CwBasicWidget Defines common behavior for widgets implemented
directly by the platform
CwShell Defines common behavior for the top-level widgets of
windows and dialogs
CwOverrideShell A pop-up window that bypasses window management,
and normally appears on top of all other widgets
CwWMShell Defines common behavior for shell widgets that do not
bypass window management
CwTopLevelShell Provides a normal window with standard appearance and
decorations
CwTransientShell A pop-up dialog window that does not bypass window
management
CwDialogShell A pop-up window used to implement modal and
modeless dialog windows

Overview of Common Widgets user interface concepts


All user interfaces have two directions of communication: from the application to
the user, and from the user back to the application. Using Common Widgets, these
two directions of communication work as follows:
v The application creates and configures user interface components (widgets)
through the use of resources and functions, and expresses interest in receiving
notification of user actions by registering callbacks and event handlers.

Chapter 7. Common Widgets 137


v The user interface notifies the application of user actions by calling callbacks and
event handlers.

The next sections explain how widgets are created and configured, and how
callbacks and event handlers allow the application to react to user actions.

The parent-child widget tree


A widget tree is created in a top-down manner. First a shell widget is created.
Next, a single child widget, usually a subclass of CwComposite, is created as the
child of the shell. This process continues until the application has created all the
widgets in the tree.

In the following example, the widget tree for a graphics application window is
shown. A CwTopLevelShell is created to interact with the window manager. A
CwMainWindow is created as the single child of the shell. A CwForm is created as
the child of the main window. The CwForm is required to position a
CwDrawingArea and a CwRowColumn, which are created as children of the form.
Three CwPushButtons are created as children of the row-column widget.
Widget tree
CwTopLevelShell
decorations and title bar

CwMainWindow
menu bar and work area

CwForm
positions side by side

CwDrawingArea
for drawing

positions in column
CwRowColumn
Appearance on screen
a button
CwPushButton

a button
CwPushButton

a button
CwPushButton

The widget lifecycle


For a widget to appear on the users display, it must be created, managed,
mapped, and realized. When a widget is no longer required, it is destroyed. These
steps are described in the following section.

1. Creating a widget
The first step is to create the widget. When a widget is created, it is given a name,
and its parent-child relationship is established. A new widget adopts default
settings for any of its resources that are not explicitly set when the widget is
created. Widget resources are data that define how a widget appears and behaves.
Resources are described in Widget resources and functions on page 141.

Widget names serve several purposes:


v Some widgets use their name to establish a default resource value. For example,
shells that have titles use their name as the default title. Push button and label
widgets use their name as the default labelString. Using the name in this way
results in more efficient code.

138 IBM Smalltalk: Programmers Reference


v Widget names are used as part of a widgets printable representation (using
printOn:). They are also useful for identifying widgets during debugging.
v On platforms supporting OSF/Motif as the native widget system, widget names
are used in conjunction with X resource files to set initial resource values for
widgets.

Tip: If you plan to use widget names to set initial resource values through X
resource files, such as .Xdefaults, then you must take care to use names which
follow the X resource file naming conventions.

When a widget is created, it does not immediately appear on the screen. In the
following illustration, a label widget named CwLabel 3 has been created, along with
two others, in a widget tree. Its parent is a row-column widget named
CwRowColumn. A row-column widget lays out its children horizontally, vertically,
or both. The row-columns parent is a shell widget named CwTopLevelShell.
Widget tree is created in memory only; Nothing is visible on the
no windows are associated with it screen at this point

CwTopLevelShell

CwRowColumn

CwLabel 1 CwLabel 2 CwLabel 3

2. Managing a widget
To manage a widget, you specify that its size and position will be managed by its
parent widget. This process is called geometry management. Any changes in the
size or position of the parent widget will be recursively propagated to managed
child widgets. The application must specify that a widget is to be managed by
sending the manageChild message to the widget.

Managing or unmanaging a widget does not affect the managed state of child
widgets. However, if a widget is unmanaged then neither it nor any of its children
will participate in geometry management. By default a widget is not managed
when it is created.

3. Mapping a widget
To map a widget, you specify that it is to appear on the display after it has been
realized. Realizing a widget is described in 4. Realizing a widget. By default, a
widget is mapped automatically when it is managed. This is controlled by the
setting of the mappedWhenManaged widget resource, which defaults to true.
Unmanaging a widget also unmaps it.

It is possible for widgets to be created, managed, and realized, but left unmapped.
The widget remains invisible until it is mapped. This technique is useful for
quickly displaying frequently used dialog boxes.

Widgets are mapped using the mapWidget method, and unmapped using the
unmapWidget method. Mapping or unmapping a widget maps or unmaps all child
widgets.

4. Realizing a widget
Until a widget is realized, it is invisible. A widget can be created, managed in the
tree, and mapped, but it will not appear on the screen until it is realized. During
realization, all widgets assume their initial geometry and create their visual

Chapter 7. Common Widgets 139


appearance. Widgets are realized by sending the realizeWidget message to the
topmost widget in the hierarchy, or shell widget. Realizing a widget realizes all of
its children recursively. Widgets created, mapped, and managed as children of
already realized widgets are automatically realized on creation.

In the following example, the CwTopLevelShell widget has been realized, and the
CwRowColumn has positioned its three label children.

Widget tree is created, managed, and mapped The widget tree is displayed
and is now realized on the screen

CwTopLevelShell
CwLabel 1
CwRowColumn
CwLabel 2

CwLabel 1 CwLabel 2 CwLabel 3 CwLabel 3

5. Destroying a widget
When a widget is no longer required, it is destroyed. A widget can be implicitly
destroyed by the user, for example by clicking on a close box. Alternatively, a
widget can be destroyed under application control by sending it the destroyWidget
message. The widget is removed from the display and released from memory.
Destroying a widget recursively destroys all child widgets.

Widget tree is destroyed Widget hierarchy is removed


and released from memory from the screen

Mapping and unmapping widgets


A widget that is managed but not mapped still participates in geometry
management, that is, it takes up space in its parent, but it is not actually drawn.
Normally this results in a blank area in the parent where the widget would
otherwise appear.

In the following example, assume that the widget tree is created, managed and
realized, but the second CwLabel is subsequently unmapped. Notice that the label
is removed from the screen, but its parent still reserves its position in the

140 IBM Smalltalk: Programmers Reference


row-column widget.

Widget tree is in memory How the widget tree


and CwLabel 2 is unmapped appears on the display

CwTopLevelShell
CwLabel 1
CwRowColumn

CwLabel 1 CwLabel 2 CwLabel 3 CwLabel 3

Managing and unmanaging widgets


When a widget is unmanaged, its parent can reclaim the space the widget
occupies. If the parent widget is a composite that performs layout of its children, it
will adjust child layout accordingly. A row-column was chosen for this example
because it provides a visual demonstration of the difference between mapping and
managing widgets.

In this example, assume that the widget tree is created, managed and realized, but
CwLabel 2 is subsequently unmanaged. Not only is the button removed from the
screen, or unmapped, but it also loses its position in the row-column widget.
The widget tree is in memory How the widget tree
and CwLabel 2 is unmanaged appears on the display

CwTopLevelShell
CwLabel 1

CwRowColumn
CwLabel 3

CwLabel 1 CwLabel 2 CwLabel 3

Widget resources and functions


Widgets are configured and controlled by the application through resources and
functions. Widget resources define the behavior and appearance of a widget.
Widget functions are messages that can be sent to a widget to tell it to do
something.

In IBM Smalltalk, resource accessors and functions are implemented as Smalltalk


methods in widget classes. Widget resource methods provide access to a widgets
resources. All methods that are not resource methods are widget function
methods. A methods comments and its message specification indicate whether it is
a resource method or a function method.

Resources
Resources are somewhat analogous to Smalltalk instance variables, and resource
set and get methods are similar to instance variable accessor methods. However,
there are several important differences:
v Resources might or might not be implemented using instance variables, and the
implementation varies from platform to platform.
v Changes to values in widget resources are immediately reflected in the
appearance of the widget.

Chapter 7. Common Widgets 141


v On platforms running Motif as the native widget system, IBM Smalltalk widget
resource values can be set using standard X resource files, like any other Motif
application.

All widgets have a core set of resources. For example, all widgets have width and
height resources. A widget can also have resources specific to its behavior. For
example, the items resource of the CwList widget defines what items are displayed
in the widget. Default values are provided for all of a widgets resources.

Resources defined by a widgets superclasses are inherited. For example, consider


the widget class CwPushButton. This class is a subclass of CwLabel, CwPrimitive,
CwBasicWidget, and CwWidget. CwPushButton inherits resources from all of its
superclasses, and defines additional resources of its own. The default value of an
inherited resource can be overridden.

The following table illustrates the resources that are available for a CwPushButton
widget. Many of the resources are provided by CwWidget, and these are available
to all widgets.
Table 31. Widget resources for the CwPushButton class
CwWidget CwPrimitive CwLabel CwPushButton.
ancestorSensitive accelerator
borderWidth backgroundColor acceleratorText activateCallback
depth alignment armCallback
destroyCallback foregroundColor fontList
height helpCallback labelIcon disarmCallback
labelInsensitiveIcon
mappedWhenManaged navigationType showAsDefault
resizable traversalOn labelInsensitivePixmap
resizeCallback labelPixmap
sensitive labelString
userData labelType
width marginBottom
x marginHeight
y marginLeft
marginRight
(plus 16 attachment- marginTop
related resources) marginWidth
mnemonic
recomputeSize

Note: Appendix A. Widget resources and callbacks on page 469 provides a table
for each class in the CwWidget hierarchy.

A widgets resources are set or retrieved using the set and get accessor methods of
the widget, which are the names that correspond directly to the associated resource
name. Resource setting methods have a special property that can be sent to a
widget to set initial state during widget creation, from inside a create argBlock.

Not all widget resources can be modified or retrieved at any time. Widget
resources have a resource access designator that indicates when the resource can be
set or retrieved. Resources are tagged with the letters C, S, or G to indicate when
the resource can be modified or retrieved, as follows:
v The application can set the resource at creation time only (C).
v The application can set the resource at any time (S).
v The application can retrieve, or get, the resource at any time after the widget is
created (G).

142 IBM Smalltalk: Programmers Reference


Resources are manipulated using get and set accessor methods derived from the
OSF/Motif name for the resource by removing the XmN prefix. For example, the
Motif resource XmNheight for a widget is retrieved and modified using the height
and height: methods, respectively. The specification for each resource method
provides the resource access designation information (C, S, G).

Create-only (C) resources can only be set using an argBlock at widget creation time.
An argBlock is a single argument Smalltalk block of code that is evaluated with the
widget being created as its argument. Resources with an (S) designation can also
be set in the argBlock. The argBlock is evaluated before the widget is fully created,
that is, while it is still under construction, but after a Smalltalk object has been
created to represent the widget. If argBlock is not required, nil can be used for the
argBlock argument, rather than unnecessarily creating an empty block.

Tip: Always set resources in the create argBlock wherever possible. Also, unless
names are needed for X resource files, use the widgets name to establish a
default resource value as described in the widget creation section on page 1.
Creating a widget on page 138. If the system has more information available
at the time of widget creation, it can perform more optimization. On some
platforms a significant performance advantage is achieved by setting
resources in the create argBlock rather than immediately after creation, which
might cause default widget configuration to have to be undone.

In the following example, the width and height resources for a drawing area are
explicitly set in an argBlock when the drawing area widget is created. These specify
the size in pixels of the drawing area widget. The size of the shell widget is
calculated based on the size of the drawing area widget. In general, when the size
of a widget is not explicitly specified, it is calculated based on the size of its
children, recursively. The string arguments in the creation messages specify the
names of the widgets. By default, the name of the top-level shell appears as the
window title.
| shell drawingArea |
shell := CwTopLevelShell
createApplicationShell: 'ShellName'
argBlock: nil.
drawingArea := shell
createDrawingArea: 'draw'
argBlock: [:w | w width: 100; height: 100].
drawingArea manageChild.
shell realizeWidget

Resources with set (S) and get (G) designations can be set and retrieved,
respectively, after widget creation using the appropriate set and get methods.

Multiple resources with set (S) designation can also be set simultaneously after the
widget is created using the setValuesBlock: message, which takes an argBlock as
argument. The setValuesBlock: method is the recommended way of setting multiple
resources for a widget after the widget is created. Normally, after a widget has
been created and a resource is modified, which changes a widgets appearance, the
widget is redisplayed to show the change. Using a setValuesBlock is more efficient
than setting the resources outside the block because the widget can then optimize
updates together, even if several of them change the widgets appearance. The
block passed to setValuesBlock: has the same format as the argBlock used when
creating a widget.

In the following example, the geometry of a shell widget is changed. Assume that
the variable shell is a top-level shell that has been created and realized.

Chapter 7. Common Widgets 143


"Set widget geometry using a series of set accessor methods.
The shell is redrawn up to four times, after for each resource change."
shell
x: 10;
y: 10;
width: 100;
height: 100.
"Set widget geometry using a set values block.
The shell is redrawn only after, with the final dimensions,
at the final position."
shell
setValuesBlock: [:w |
w
x: 10;
y: 10;
width: 100;
height: 100].

Tip: Some resources change their values when the value of a different resource in
the same widget is changed. To avoid this push-down-here-pop-up-there
effect, such resources must be set simultaneously using an argBlock, either on
creation or after creation using setValuesBlock:.This situation occurs with
left/right and top/bottom CwForm attachment resources, which should
always be set in pairs.

Function methods
Widget methods that are not resource set or get methods are widget function
methods. Unlike resource setting messages, function messages can only be sent to
widgets after they have been created. While resource methods are used to access or
change widget state, function methods typically perform more complex operations,
and in some cases modify resource values. While resource get and set methods
uniformly require zero arguments and one argument respectively, widget function
methods take varying numbers of arguments, depending on the particular
function. The manageChild method is an example of a widget function.

Functions often alter the resource values of a widget as a side effect. For example,
the setString: function for text widgets alters the value resource of the widget. In
some cases it is possible to achieve the same effect using either a resource method
or a function method.

Tip: Do not call function methods from inside a create argBlock. Because the
widget is not fully created when the create argBlock is evaluated, invoking
widget functions results in errors.

CwConstants pool dictionary


The Common Widgets subsystem uses a pool dictionary called CwConstants to
provide pool variables for constant values. For example, pool variables such as
XmATTACHFORM and XmNactivateCallback are used as arguments to Common
Widgets methods. These pool variable names should be used rather than directly
using their constant values. All classes that require these Common Widgets pool
variable names must include the CwConstants pool dictionary in their class
definition.

Example code to create a widget tree


The following code illustrates how to create the example widget tree shown above:

144 IBM Smalltalk: Programmers Reference


| shell main form drawArea rowColumn button1 button2 button3 |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Graphics Example'].
"A top-level shell is created. A mainWindow widget is created. The parent is shell."
main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.
"A form widget is created as a child of main."
form := main
createForm: 'form'
argBlock: nil.
form manageChild.
"A drawArea widget is created as a child of form."
drawArea := form
createDrawingArea: 'drawing'
argBlock: [:w |
w
"Its width and height are set to 300 pixels."
width: 300;
height: 300;
"Its border width is set to 1 pixel."
borderWidth: 1].
drawArea manageChild.
"A rowColumn widget is created as a child of form."
rowColumn := form
createRowColumn: 'buttons'
argBlock: nil.
rowColumn manageChild.
"In addition, three push buttons are created as children of rowColumn.
By default, the names of the buttons ('1', '2', '3')
will appear as the button labels. rowColumn will
determine its size based on the sizes of the buttons."
button1 := rowColumn
createPushButton: '1'
argBlock: nil.
button1 manageChild.
button2 := rowColumn
createPushButton: '2'
argBlock: nil.
button2 manageChild.
button3 := rowColumn
createPushButton: '3'
argBlock: nil.
button3 manageChild.
"Form constraints for each child widget within form are specified.
rowColumn is offset from the edge of form by 2 pixels on all sides,
and attached to the edge of form on the top, bottom, and left sides."
rowColumn
setValuesBlock: [:w |
w
topAttachment: XmATTACHFORM;
topOffset: 2;
bottomAttachment: XmATTACHFORM;
bottomOffset: 2;
leftAttachment: XmATTACHFORM;
leftOffset: 2].
"drawArea is attached to form on the top, bottom and right. On the left side,
it is attached to rowColumn."
drawArea
setValuesBlock: [:w |

Chapter 7. Common Widgets 145


w
bottomAttachment: XmATTACHFORM;
bottomOffset: 4;
topAttachment: XmATTACHFORM;
topOffset: 2;
rightAttachment: XmATTACHFORM;
rightOffset: 4;
leftAttachment: XmATTACHWIDGET;
leftWidget: rowColumn;
leftOffset: 2].

"Finally, the realizeWidget message is sent to the topmost widget.


All widgets are now shown on the screen."
shell realizeWidget

Tip: Form constraints are described in detail in Form widgets on page 167.

When this code is run, a window titled Graphics Example appears on the screen.
The widgets behave as expected, but the application is not notified when a button
is pressed or when the mouse is moved. For the application to be notified of the
users interaction with the widgets, event handlers and callbacks are required.

Graphics Example
1
2
3

Widget event handling and callbacks


An event is the mechanism that notifies the application when the user performs a
mouse or keyboard operation. The application can be notified about key presses
and releases, mouse button presses and releases, and mouse movements. Events
are handled by adding an event handler to a widget.

A callback is the mechanism that notifies the application when some higher level
action is performed on a widget. For example, the XmNactivateCallback is used to
inform the application that a CwPushButton has been pressed and released. As
another example, all widgets support the XmNdestroyCallback that is invoked just
before a widget is destroyed.

The following example illustrates how callbacks and event handlers are defined.
For detailed information on callbacks and event handlers, see Callbacks on page
150 and Event handlers on page 154.

Example of using an event handler and a callback


In the following example, a small graphics application interface is created. The
widget tree created by the code is illustrated on the right.

146 IBM Smalltalk: Programmers Reference


The code to create this tree is similar to that for the example on page Example
code to create a widget tree on page 144, but in this example event and callback
handler code (bold text) has been added. This code registers the event and callback
handlers just after the widgets are created.

When the push-button widget is pressed (that is, when the user clicks mouse
button 1 while the mouse pointer is over the push-button widget), the
pressed:clientData:callData: method is run. When the mouse is moved in the drawing
area with button 1 held down, the button1Move:clientData:event: method is run.

| shell main form drawArea button | Graphics Example


shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w |
w title: 'Graphics Example'].
main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.

form := main
createForm: 'form'
argBlock: nil.
form manageChild.

drawArea := form
createDrawingArea: 'drawing'
argBlock: [:w |
w
width: 300;
height: 300;
borderWidth: 1].
"After the drawing area is created, an event handler is added to the widget
The method specifies that the application shall be notified of Button1Motion
events (button 1 is down and the mouse is moving) within the drawing area.
When mouse events occur, the button1Move:clientData:event message is sent
to self (the application)."
drawArea
addEventHandler: Button1MotionMask
receiver:self
selector: #button1Move:clientData:event:
clientData:nil.
drawArea manageChild.
button := form
createPushButton: '1'
argBlock: nil.
"Here a callback is added to the newly created push-button widget.
The method specifies that the application shall be notified of
activate callback (the button is pressed and released). When the
button is activated, the pressed:clientData:callData: message is
sent to self."
button
addCallback:XmNactivateCallback
receiver: self
selector: #pressed:clientData:callData:
clientData:nil.
button manageChild.
drawArea
setValuesBlock: [ :w |
w
topAttachment: XmATTACHFORM;
topOffset: 2;

Chapter 7. Common Widgets 147


bottomAttachment: XmATTACHFORM;
bottomOffset: 2;
leftAttachment: XmATTACHWIDGET;
leftWidget: button;
leftOffset: 2;
rightAttachment: XmATTACHFORM;
rightOffset: 2].
button
setValuesBlock: [ :w |
w
topAttachment: XmATTACHFORM;
topOffset: 2;
leftAttachment: XmATTACHFORM;
leftOffset: 2].
shell realizeWidget.

Creating and using widgets


The previous section provided an overview of how widgets are created and
configured, and how they interact with an application. This section describes how
to create and use specific widgets in the Common Widgets subsystem. A more
detailed description of callbacks and event handlers is also provided.

The following widgets are discussed:


Shells
CwTopLevelShell, CwDialogShell, CwOverrideShell
Main windows and scrolled windows
CwMainWindow and CwScrolledWindow
Text editors and drawing areas
CwText andCwDrawingArea
Layout widgets
CwForm and CwRowColumn
Buttons and labels
CwLabel, CwDrawnButton, CwPushButton, CwToggleButton
Menus and menu items
CwRowColumn, CwCascadeButton, CwPushButton
Lists and combo boxes
CwList and CwComboBox
Composite boxes
CwBulletinBoard, CwCompositeBox, CwMessageBox, CwSelectionBox

Note: These widgets have many resources and callbacks. This section discusses
only the most commonly used resources and callbacks. For a complete list,
see Appendix A. Widget resources and callbacks on page 469.

Widget creation convenience methods


Widgets might be created using convenience methods implemented as instance
methods of CwWidget or as class methods of CwShell and its subclasses. These
convenience methods are sent to the widget that will be the new widgets parent.
As a shell does not need a parent, shell creation convenience methods are sent to
the class. Convenience methods are provided to create the following:
v All of the standard widgets; for example, CwPushButton, CwList, and
CwTopLevelShell

148 IBM Smalltalk: Programmers Reference


v Commonly used configurations of some standard widgets; for example,
CwRowColumn widgets preconfigured for use as menus or radio boxes
v Special pseudowidgets that appear to be composite arrangements of standard
widgets; for example, scrolled list and scrolled text. Due to platform
optimizations, such pseudowidgets might not actually be implemented as
composites of platform widgets

Because convenience methods do not automatically manage the widgets that they
create, the new widgets must be explicitly managed after they are created. In
addition, because some convenience functions actually create composite widget
subtrees, the widget returned might not be a direct child of the widget the creation
message was sent to. For an example of this, see Scrolled lists on page 188.

Tip: Widgets can also be created by sending messages directly to the widget
classes. However, widgets created in this way bypass normal initialization
and configuration, and might have different behavior than widgets created
using convenience functions. Always use the convenience function to create a
widget, if one is available.

The code fragment below illustrates how to create a push-button widget using a
convenience method. Top level shells are created by sending a message directly to
the CwTopLevelShell class.
| shell button |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: nil.
button := shell
createPushButton: 'Button1'
argBlock: nil.
button manageChild.
shell realizeWidget.

Some convenience methods do more than create a widget. For example, the
createScrolledList:argBlock: method creates a pseudowidget that appears, in many
respects, to be a scrolled window, scroll bars, and a list widget. The relationship
between the scrolled window, the scroll bars, and the list is defined such that the
list can be scrolled using the scroll bars of the scrolled window. The create
operation returns the list widget, and is treated by the application as any other list
widget. However, the widget system created is tightly coupled and on some
platforms might be implemented using a single widget, to maximize performance
and platform look and feel. For these reasons, the application should not attempt
to reconfigure the hidden parts of such pseudowidgets.

Tip: Do not attempt to reconfigure the hidden parts of pseudowidgets such as


scrolled lists or scrolled texts. A scrolled text or list might be implemented
using a single platform widget and might not behave in exactly the same way
as a CwText or CwList created as a child of a CwScrolledWindow.

Following is a list of the shell widget creation convenience methods that are class
methods of CwShell and its subclasses:
createPopupShell:parent:argBlock:
createApplicationShell:argBlock:

Following is a list of the widget creation convenience methods that are instance
methods of CwWidget:
createArrowButton:argBlock:
createBulletinBoard:argBlock:

Chapter 7. Common Widgets 149


createBulletinBoardDialog:argBlock:
createCascadeButton:argBlock:
createComboBox:argBlock:
createDialogShell:argBlock:
createDrawingArea:argBlock:
createDrawnButton:argBlock:
createErrorDialog:argBlock:
createForm:argBlock:
createFormDialog:argBlock:
createFrame:argBlock:
createInformationDialog:argBlock:
createLabel:argBlock:
createList:argBlock:
createMainWindow:argBlock:
createMenuBar:argBlock:
createMessageBox:argBlock:
createMessageDialog:argBlock:
createOptionMenu:argBlock:
createPopupMenu:argBlock:
createPromptDialog:argBlock:
createPulldownMenu:argBlock:
createPushButton:argBlock:
createQuestionDialog:argBlock:
createRadioBox:argBlock:
createRowColumn:argBlock:
createScale:argBlock:
createScrollBar:argBlock:
createScrolledList:argBlock:
createScrolledText:argBlock:
createScrolledWindow:argBlock:
createSelectionBox:argBlock:
createSelectionDialog:argBlock:
createSeparator:argBlock:
createSimpleCheckBox:argBlock:
createSimpleMenuBar:argBlock:
createSimpleOptionMenu:argBlock:
createSimplePopupMenu:argBlock:
createSimplePulldownMenu:argBlock:
createSimpleRadioBox:argBlock:
createText:argBlock:
createToggleButton:argBlock:
createWarningDialog:argBlock:
createWorkArea:argBlock:
createWorkingDialog:argBlock:

Callbacks
Actions performed on widgets by the user must be communicated back to the
application. One mechanism used for this communication is a callback. A callback
method defines actions to perform in response to some occurrence in a widget.
Callbacks are normally registered just after widgets are created. For example, when
a push-button widget is created, the application usually registers an activate
callback that is run when the button is activated by the user clicking on it.
Although it is not necessary for the application to register callbacks, without them
the application is unable to take action based on the users interaction with the
widgets.

150 IBM Smalltalk: Programmers Reference


Callbacks are registered using the addCallback:receiver:selector:clientData: method.

Tip: The argBlock argument of a widget creation message can only be used to set
widget resources. The addCallback: message cannot be used within the create
argBlock. Callbacks are usually registered immediately after the widget has
been created, and before it is realized.

The addCallback:receiver:selector:clientData: method takes four parameters:


callbackName
A constant specifying which callback is being registered, for example,
XmNactivateCallback
receiver
The object that will receive the callback message
selector
The 3-parameter callback message selector
clientData
Optional data that will be passed to the callback when it is run

When a callback method is run, it is passed three arguments:


v The widget that caused the callback
v The client data specified when the callback was registered
v Information specific to the type of callback, called the call data

The following example illustrates how to register a callback. First a button is


created, in this case, as the child of a shell, and then an XmNactivateCallback is
added to the button.
| shell button |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: nil.

button := shell
createPushButton: 'OK'
argBlock: nil.
button
"Name of the callback to add"
addCallback: XmNactivateCallback
"Receiver of the callback message, usually self"
receiver:self
"Name of method to execute"
selector: #pressed:clientData:callData:
"Data to be passed unmodified to the callback method"
clientData: 'Test data'.
button manageChild.

shell realizeWidget.

When an activate callback occurs due to the button being pressed, the
pressed:clientData:callData: method, shown below, is run. The method prints the
string Test data on the Transcript Window. The widget issuing the callback is
passed as the widget parameter. In this case, this is the push-button widget. The
string Test data, specified as the client data when the callback was added, is
passed as the clientData parameter. Callback-specific data is passed as the callData
parameter. For the activate callback of push-button widgets, however, the call data
provides no new information.

Chapter 7. Common Widgets 151


pressed: widget clientData: clientData callData: callData
"The push button has been pressed."
Transcript cr; show: clientData

The following table describes the class hierarchy and data accessor method names
for call data objects. All classes are concrete classes. A description of each accessor
method can be found in the source or comment for each method.
Table 32. Call data class hierarchy
Class hierarchy Responsibility Data accessor methods
CwAnyCallbackData Provides call data for most reason (a constant,
callbacks. prefixed by XmCR)
CwComboBoxCallbackData Provides call data for combo item
box singleSelectionCallback. itemPosition
CwConfirmationCallbackData Provides call data for doit
callbacks such as the shell doit:
windowCloseCallback. This
callback can be canceled by
the application.
CwTextVerifyCallbackData Provides call data for text currInsert
and combo-box endPos
modifyVerifyCallback. These startPos
callbacks can be canceled by text
the application. text:
CwDrawingCallbackData Provides call data for event
callbacks such as composite window
expose and interceptExpose
callbacks, drawing area input
callbacks, and drawn-button
activate and expose
callbacks.
CwListCallbackData Provides call data for item
callbacks such as list itemPosition
browseSelect, singleSelect, selectedItemCount
multipleSelect,
extendedSelect, and selectedItemPositions
defaultAction callbacks. selectedItems
CwRowColumnCallbackData Provides call data for widget
callbacks such as data
row-column entryCallback. callbackData
CwToggleButtonCallbackData Provides call data for set
callbacks such as the
toggle-button
valueChangedCallback.
CwValueCallbackData Provides call data for value
callbacks such as scale and
scroll bar drag and
valueChangedCallback, and
scroll bar decrement,
increment, pageDecrement,
pageIncrement, toBottom,
and toTop callbacks.

Tip: Call data objects are only valid during the callback. Do not store a call data
object in a callback method and attempt to reference it later.

152 IBM Smalltalk: Programmers Reference


The following table lists the callbacks supported by each widget.
Table 33. Callbacks supported by each widget
Widgets Callbacks supported
CwArrowButton activate, arm, destroy, disarm, help, resize
CwBasicWidget destroy, resize
CwBulletinBoard destroy, expose, focus, help, interceptExpose, losingFocus,
map, resize, unmap
CwCascadeButton cascading, destroy, help, resize
CwComboBox activate, destroy, focus, help, losingFocus, modifyVerify,
popdown, popup, resize, singleSelection, valueChanged
CwComposite destroy, expose, focus, help, interceptExpose, losingFocus,
resize
CwCompositeBox destroy, expose, focus, help, interceptExpose, losingFocus,
map, ok, resize, unmap
CwDialogShell destroy, focus, iconify, popdown, popup, resize,
windowClose
CwDrawingArea destroy, expose, focus, help, input, interceptExpose,
losingFocus, resize
CwDrawnButton activate, arm, destroy, disarm, expose, focus, help,
losingFocus, resize
CwForm destroy, expose, focus, help, interceptExpose, losingFocus,
map, resize, unmap
CwFrame destroy, expose, focus, help, interceptExpose, losingFocus,
resize
CwLabel destroy, help, resize
CwList browseSelection, defaultAction, destroy, extendedSelection,
help, multipleSelection, resize, singleSelection
CwMainWindow destroy, expose, focus, help, interceptExpose, losingFocus,
resize
CwMessageBox cancel, destroy, expose, focus, help, interceptExpose,
losingFocus, map, ok, resize, unmap
CwOverrideShell destroy, popdown, popup, resize
CwPrimitive destroy, help, resize
CwPushButton activate, arm, destroy, disarm, help, resize
CwRowColumn destroy, entry, expose, focus, help, interceptExpose,
losingFocus, map, resize, simple, unmap
CwScale destroy, drag, expose, focus, help, interceptExpose,
losingFocus, resize, valueChanged
CwScrollBar decrement, destroy, drag, help, increment, pageDecrement,
pageIncrement, resize, toBottom, toTop, valueChanged
CwScrolledWindow destroy, expose, focus, help, interceptExpose, losingFocus,
resize
CwSelectionBox apply, cancel, destroy, expose, focus, help, interceptExpose,
losingFocus, map, noMatch, ok, resize, unmap
CwSeparator destroy, help, resize
CwShell destroy, popdown, popup, resize

Chapter 7. Common Widgets 153


Table 33. Callbacks supported by each widget (continued)
Widgets Callbacks supported
CwText activate, destroy, help, focus, losingFocus, modifyVerify,
resize, valueChanged
CwToggleButton arm, destroy, disarm, help, resize, valueChanged
CwTopLevelShell destroy, focus, iconify, popdown, popup, resize,
windowClose
CwWidget destroy, dragDetect, resize
CwWMShell destroy, focus, iconify, popdown, popup, resize,
windowClose

Event handlers
Event handlers are another mechanism used to inform the application of input
actions by the user. While callbacks notify the application of high level interactions
such as the selection of items in a list widget, event handlers notify the application
of low level interactions, including the following:
v Mouse pointer motion
v Mouse button presses and releases
v Individual key presses and releases

Register event handlers using theaddEventHandler:receiver:selector:clientData: method.

Tip: The argBlock argument of a widget-creation message can only be used to set
widget resources. The addEventHandler: message cannot be used within the
create argBlock. Event handlers are usually registered immediately after the
widget has been created, and before it is realized.

The addEventHandler:receiver:selector:clientData: method takes four parameters:


eventMask
A bit mask specifying which events to notify the receiver of
receiver
The object that receives the event handler message
selector
The 3-parameter event handler message selector
clientData
Optional data that is passed to the event handler when it is run

The eventMask is specified as the bitwise-OR of one or more of the bit masks
described in the following table.
Table 34. Common widgets event masks
Event masks Description
KeyPressMask Keyboard key down events
KeyReleaseMask Keyboard key up events
ButtonPressMask Mouse button down events
ButtonReleaseMask Mouse button up events
PointerMotionMask All pointer motion events
Button1MotionMask Pointer motion events while button 1 is down

154 IBM Smalltalk: Programmers Reference


Table 34. Common widgets event masks (continued)
Event masks Description
Button2MotionMask Pointer motion events while button 2 is down
Button3MotionMask Pointer motion events while button 3 is down
ButtonMotionMask Pointer motion events while any button is down
ButtonMenuMask Button menu request events

When an event handler method is run, it is passed three arguments:


v The widget to which the handler was added and in which the event occurred
v The client data specified when the event handler was registered
v An object describing the event, called the event

The following table describes the class hierarchy for event objects. Classes in italics
are abstract classes.
Table 35. Event class hierarchy
Class hierarchy Responsibility.
CwEvent Defines common behavior for event data in event
handlers.
CwExposeEvent Provides event data for expose events in expose
callbacks (see Note below).
CwInputEvent Defines common behavior for button, key, and
motion event objects.
CwButtonEvent Provides event data for mouse button-press/release
events.
CwKeyEvent Provides event data for key-press/release events.
CwMotionEvent Provides event data for mouse motion events.

Tip: An expose event handler cannot be explicitly added to a widget. A


CwExposeEvent object is passed to an application as part of the call data for an
exposeCallback.

The following messages can be sent to the event object to retrieve information
about the event. The methods for all events (CwEvent) include the following:
type The type of event that occurred. This has one of the following values:
ButtonPress, ButtonRelease, Expose, KeyPress, KeyRelease, and MotionNotify.
window
The CgWindow associated with the widget for which the event was
generated.
display The CgDisplay associated with the event.

For expose events (CwExposeEvent), the method include the following:


count The number of expose events which remain for the affected CgWindow. A
simple application might want to ignore all expose events with a nonzero
count, and perform a full redisplay if the count is zero.
rectangle
A rectangle describing the damaged area, in the coordinate system of the
affected CgWindow.
x The x-coordinate of the origin of the damaged rectangle.
y The y-coordinate of the origin of the damaged rectangle.

Chapter 7. Common Widgets 155


height The height, in pixels, of the damaged rectangle.
width The width, in pixels, of the damaged rectangle.

For input events (CwButtonEvent, CwKeyEvent, and CwMotionEvent), the methods


are as follows:
state A bit mask representing the logical state of modifier keys and pointer
buttons just prior to the event. Possible bit masks include: ControlMask,
ShiftMask, LockMask, Mod1Mask to Mod5Mask, and Button1Mask to
Button3Mask.
x The x-coordinate of the pointer, relative to the widget in which the event
occurred.
y The y-coordinate of the pointer, relative to the widget in which the event
occurred.
point x @ y
xRoot A coordinate of the pointer, relative to the screen.
yRoot A coordinate of the pointer, relative to the screen.
pointRoot
xRoot @ yRoot
time The time, in milliseconds, at which the event occurred.

For mouse button events (CwButtonEvent), the methods are as follows:


button The number of the button that was pressed or released (1, 2 or 3).

For key events (CwKeyEvent), the methods are as follows:


keysym A constant describing the keyboard key that was pressed or released. These
constants are found in the CwConstants pool dictionary, and are prefixed
with XK.
character
The Character describing the keyboard key that was pressed or released, or
nil if it does not represent a valid character.

There are two common uses of event handlers. The first is for handling input in a
drawing area widget. For example, in a graphical drawing application a drawing
area widget would be used to display the drawing under construction. Event
handlers would be registered to notify the application of pointer motion, mouse
button, and key press events, allowing text strings to be edited and graphical
objects to be positioned and changed using the mouse.

The second common use is for handling pop-up menus. An event handler is added
for the ButtonMenuMask event. When the event handler is called, the application
pops the menu up.

Mouse button 3 is used as the menu button. However, some platforms trigger the
button menu event when the button is pressed, and others when the button is
released. The ButtonMenuMask event hides this difference. It should be used, rather
than the other button events, to support pop-up menus in a platform-independent
manner.

Tip: On some platforms it is possible for a button release event to be delivered


without a corresponding button press event. Applications should be prepared

156 IBM Smalltalk: Programmers Reference


to ignore such spurious button release events by only processing a button
release event that is received after a matching button press event.
In the following example, a drawing area is created, and an event handler is added
to notify the application of mouse button presses, key presses, and pointer motion.
Label widgets are used to display information about the events. The variable labels
would be implemented as an instance variable for the class.
| shell rowColumn label labels drawing |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Event Handler Example'].
rowColumn := shell
createRowColumn: 'rowColumn'
argBlock: nil.
rowColumn manageChild.
labels := Array new: 3.
1 to: 3 do: [:i |
label := rowColumn
createLabel: 'label'
argBlock: nil.
label manageChild.
labels at: i put: label].
(labels at: 1) labelString: 'Position: '.
(labels at: 2) labelString: 'Button pressed at position: '.
(labels at: 3) labelString: 'Keysym of last pressed key: '.
drawing := rowColumn
createDrawingArea: 'drawing'
argBlock: [:w |
w
borderWidth: 1;
width: 300;
height: 300].
drawing
addEventHandler: ButtonPressMask | KeyPressMask | PointerMotionMask
receiver: self
selector: #eventHandler:clientData:event:
clientData: nil.
drawing manageChild.
shell realizeWidget

When an event occurs, the following method is run. Information about the event is
determined from the event argument and is displayed in the label widgets.
eventHandler: widget clientData: clientData event: event
"Handle an input event."

event type = MotionNotify


ifTrue: [(labels at: 1) labelString: 'Position: ',
event point printString].

event type = ButtonPress


ifTrue: [(labels at: 2) labelString: 'Button ', event
button printString, ' pressed at position: ',
event point printString].

event type = KeyPress


ifTrue: [(labels at: 3) labelString: 'Keysym of last pressed key: ',
event keysym printString].

Chapter 7. Common Widgets 157


Shell widgets
Shell widgets provide the interface between an application and the platforms
window manager. A shell widget looks like a window on the screen and contains
exactly one child. The window manager is the part of the platform window system
that manages the geometry, appearance, and stacking order of windows on the
display. The window manager can add window decorations to a window, such as a
frame, a title, resize handles, minimize and maximize buttons, and a close button.
Window decorations are described in more detail in Top-level shell widgets on
page 158. The window manager also keeps track of which window has input focus,
that is, which window receives keyboard input. A shell can receive a focus callback
when focus is either lost or gained.

A CwTopLevelShell provides a normal window with standard appearance and


decorations, and does not have a parent. CwTopLevelShell widgets are described in
detail in the next section. CwOverrideShell and CwDialogShell widgets must have a
parent widget. These shells are described in this section.

CwDialogShell widgets are pop-up windows used to implement modal or modeless


dialog windows. The child of a CwDialogShell is typically an instance of a subclass
of CwBulletinBoard. A CwDialogShell and its child are typically created automatically
by using one of the dialog convenience methods. Unlike other types of shells, a
CwDialogShell popped up by managing its child. The parent of a CwDialogShell can
be any widget, and the dialog always appears over the window containing its
parent widget. For further information on dialog shells and dialogs, see the section
on Composite-box widgets on page 190.

CwOverrideShell widgets are used for pop-up windows that bypass window
management and appear in front of all other windows. They do not have a
window frame, and cannot be moved, resized, or iconified by the user. Create
CwOverrideShell widgets using the CwShell class method
createPopupShell:parent:argBlock:. Make a CwOverrideShell visible by sending it the
popup message.

Top-level shell widgets


This section describes how to create and use top-level shell widgets
(CwTopLevelShell). Some commonly used shell resources and callbacks are
discussed. A list of all resources and callbacks used by top-level shells is in
Appendix A. Widget resources and callbacks on page 469.

The root of a widget tree must be a CwTopLevelShell. A top level shell widget has
no parent. Top-level shells are created using the createApplicationShell:argBlock:
method, which is sent to the CwTopLevelShell class. A top-level shell widget must
have a child widget before it can be realized.

Tip: A common programming error is to attempt to realize a top level shell that
has no child widget, or whose child widget computes an initial size with zero
width or height. On a Motif platform this normally causes the application to
exit with the message: Error: Shell widget has zero width or height. IBM
Smalltalk detects most common cases and prevents the application from
exiting.

The following example creates a top-level shell with a main window widget as its
child. In this example, the main windows width and height are explicitly set.

158 IBM Smalltalk: Programmers Reference


However a main window is not usually given explicit dimensions. It is usually left
to calculate its dimensions based on the needs of its children.
| shell mainWindow |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Shell Example 1'].
mainWindow := shell
createMainWindow: 'main'
argBlock: [:w | w width: 100; height: 100].
mainWindow manageChild.
shell realizeWidget.

The resources for CwTopLevelShell include title, mwmDecorations, icon, iconPixmap,


and iconMask. The mwmDecorations resource indicates which decorations are to be
added to the shell. The following table lists the bit masks used to specify
decorations. The icon (or iconPixmap) resources indicate the icon (or pixmap) to be
used by the window manager for the applications icon, and the iconMask resource
indicates the pixmap to clip to if the icon is non-rectangular.
Table 36. TopLevel shell decoration resource flags
Decoration Literal Definition
MWMDECORALL If set, changes the meaning of the other flags to indicate that
the specified decoration should be removed from the default
set.
MWMDECORBORDER Include a border.
MWMDECORRESIZEH Include resize frame handles.
MWMDECORTITLE Include title bar.
MWMDECORMENU Include window close/system menu.
MWMDECORMINIMIZE Include minimize window button.
MWMDECORMAXIMIZE Include maximize window button.

Tip: The top-level shell decorations settings indicate the preferred configuration
for the window. The window manager can alter or ignore the settings if
particular combinations are not supported by the platform.

In the following example, the shells mwmDecorations resource is explicitly altered


in the create argBlock to specify that the minimize button should not be provided.
| shell main |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w |
w
title: 'Shell Example 2';
mwmDecorations: MWMDECORALL | MWMDECORMINIMIZE].
main := shell
createMainWindow: 'main'
argBlock: [:w | w width: 100; height: 100].
main manageChild.
shell realizeWidget.

Chapter 7. Common Widgets 159


Scrolled-window widgets
The scrolled-window widget (CwScrolledWindow) can scroll any other widget by
positioning it behind a clipping area. No special processing by the application is
required to implement scrolling. Any widget tree can be scrolled simply by making
it the work region of a CwScrolledWindow or CwMainWindow widget.

Create a scrolled-window widget by sending the createScrolledWindow:argBlock:


message to its parent. Next, create the widget to be scrolled. Make it the work
region for the scrolled window by using the setAreas:verticalScrollbar:workRegion:
message.

Scrolled-window widgets support two scrolling policies, specified by the


scrollingPolicy resource. These are XmAPPLICATIONDEFINED (the default) and
XmAUTOMATIC. When the scrolling policy is XmAUTOMATIC, the scrolled
window handles all aspects of scrolling, including creation of the scroll bars. The
application can be notified of scroll bar movements by adding callbacks.

When the scrolling policy is XmAPPLICATIONDEFINED, the application must


handle all aspects of scrolling, including creation of scroll bars. Set the scroll bars
using the setAreas:... message.

The scrollBarDisplayPolicy resource defines whether or not scrollbars are always


showing (XmSTATIC) or displayed only if the work region exceeds the clip area
(XmASNEEDED).

Tip: The scrollingPolicy and scrollBarDisplayPolicy resources can only be set at


creation time.

The following example creates a scrolled window containing several buttons in a


vertical row-column. The scrolling policy is XmAUTOMATIC.

| shell scroll buttons | Scrolled Buttons


shell := CwTopLevelShell Array
createApplicationShell: 'shell'
argBlock: [:w | ArrayedCollection
w title: 'Scrolled Buttons'].
"Creates a scrolled window with automatic Bag
scrolling policy. The default
scrollBarDisplayPolicy, XmSTATIC, is used." ByteArray

scroll := shell
createScrolledWindow: 'scroll'
"The default scrollBarDisplayPolicy."
argBlock: [:w | w scrollingPolicy: XmAUTOMATIC].

buttons := scroll
"Creates a row-column that will contain the buttons to be scrolled."
createRowColumn: 'buttons'
argBlock: nil.
buttons manageChild.
(Collection withAllSubclasses collect: [:class | class name])
asSortedCollection do: [:name |
(buttons
"Creates the push buttons using the Collection class hierarchy names."
createPushButton: name
argBlock: nil)
manageChild].

160 IBM Smalltalk: Programmers Reference


"Sets the scrolled-window areas."
scroll
"Only the work region needs to be set."
setAreas: nil
"The scroll bars are created automatically because the scrolling policy
is XmAUTOMATIC."
verticalScrollbar: nil
workRegion: buttons.

scroll manageChild.
shell realizeWidget.

Main-window widgets
The main-window widget (CwMainWindow) is used to organize the applications
menu bar and the widgets that define the applications work region. As a subclass
of CwScrolledWindow, CwMainWindow class also includes all of the functionality
provided by the CwScrolledWindow class and can provide scroll bars for scrolling
the work region. If a main window is used, it must be the immediate child of a
top-level or dialog shell.

A main-window widget is created by sending the createMainWindow:argBlock:


message to a shell widget.

A CwMainWindow must always be created as the child of a CwTopLevelShell or


CwDialogShell. Creating it as the child of any other widget is an error.

Main windows and geometry management


Like other composite widgets, a main-window widget manages the geometry of its
children.
shell

In order to manage its children correctly, a main-window widget must know which
widget is the menu bar, which widget is the work region, and which widgets are
the scroll bars. The setAreas:horizontalScrollbar:verticalScrollbar:workRegion: message
explicitly tells the main window which of its child widgets are to be used for these
purposes. In the following example, an empty menu bar and a drawing area
widget are created as children of the main window. The setAreas: message is sent to
the main window to explicitly set menuBar as the main windows menu bar, and
drawingArea as the main windows work region. Because no scroll bars are being
defined by the application, nil is passed in for the scroll bar arguments. For more
information on menus and menu bars, see Menus on page 178.
| shell main menuBar drawingArea |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: nil.
main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.

Chapter 7. Common Widgets 161


menuBar := main
createSimpleMenuBar: 'menu'
argBlock: [:w | w buttons: #('')].
menuBar manageChild.
drawingArea := main
createDrawingArea: 'draw'
argBlock: [:w | w width: 300; height: 300].
drawingArea manageChild.
main
setAreas: menuBar
horizontalScrollbar: nil
verticalScrollbar: nil
workRegion: drawingArea.

shell realizeWidget.

Text widgets
The text widget (CwText) provides text viewing and editing capabilities to the
application. Create text widgets using the createText:argBlock: and
createScrolledText:argBlock: convenience methods. The latter method makes the text
scrollable, but otherwise provides basically the same functionality.

Set and retrieve the entire contents of the text widget using the setString: and
getString methods.

When a scrolled text widget is created using createScrolledText:argBlock:, a


CwScrolledWindow widget is inserted between the text widget and the original
parent. This is important to know when setting CwForm attachments, because in
this case the attachments must be set on the text widgets parent (the scrolled
window) rather than the text widget itself. See Scrolled lists on page 188 for an
example. See Form widgets on page 167 for a description of attachments.

Two of the text widgets resources are editMode and wordWrap. The editMode
resource specifies whether the widget supports single-line or multiline editing of
text. It can be set to XmSINGLELINEEDIT (the default) or XmMULTILINEEDIT.
The wordWrap resource specifies whether lines are to be broken at word breaks so
that text does not go beyond the right edge of the window. The default setting for
wordWrap is false.

Tip: Word wrap and horizontal scrolling are incompatible. In order for word wrap
to work, the text widget must be configured without a horizontal scroll bar by
setting the scrollHorizontal resource to false.

The following example creates a scrollable, multiline text widget with word wrap
on.

| shell text | Text Widget Example

shell := CwTopLevelShell Edit me!


createApplicationShell: 'shell'
argBlock: [:w |
w title: 'Text Widget Example'].

text := shell
"A scrollable text widget is created and managed."

createScrolledText: 'text'
argBlock: [:w |
w

162 IBM Smalltalk: Programmers Reference


"The widget is configured to support multiline editing and word wrap."
editMode: XmMULTILINEEDIT;
scrollHorizontal: false;
wordWrap: true].
"The contents of the widget are set to a sample string."
text setString: 'Edit me!'.
text manageChild.
shell realizeWidget.

CwText widgets also have resources to control the initial number of rows and
columns they contain, the position of the insertion point, the width of the tab
character, and whether or not the CwText is editable. For a complete list of CwTexts
resources and callbacks, see Appendix A. Widget resources and callbacks on
page 469. CwText widgets can also set, get, cut, copy, and paste a selection, scroll to
a given line, and insert or replace text at a given position.

A CwText widget has input focus when it can accept keyboard input. A CwText
usually provides some visual indication that the widget has focus, such as
displaying the insertion position as a flashing I-beam or drawing a thicker border.
Application developers can add a focusCallback or a losingFocusCallback to a CwText
if additional behaviour is required when the widget either gains or loses focus. For
further discussion on the concept of focus, see Shell widgets on page 158. You
can see an example of a losingFocusCallback with a single-line text widget in
Example: a primitive extended widget on page 203.

Two other callbacks provided by CwText widgets are modifyVerifyCallback, called


just before text is deleted from or inserted into a CwText, and valueChangedCallback,
called after text is deleted from or inserted into a CwText. The following example
uses a modifyVerifyCallback to allow only uppercase letters to be entered into a
single-line CwText.

Object subclass: #TextExample Text Widget Example

instanceVariableNames: '' CAPS ONLY


classVariableNames: ''
poolDictionaries: 'CwConstants '

open
| shell text |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Text Widget Example'].
text := shell
createText: 'text'
argBlock: [:w | w columns: 18].
text
addCallback: XmNmodifyVerifyCallback
receiver: self
selector: #modifyVerify:clientData:callData:
clientData: nil.
text manageChild.

shell realizeWidget.
modifyVerify: widget clientData: clientData callData: callData

"Update the stored version of the string in the callData, so that the
text widget inserts capital letters instead of the real text typed or
pasted by the user."

callData text: callData text asUppercase

Chapter 7. Common Widgets 163


Drawing area widgets
The drawing area (CwDrawingArea) widget provides an application with an area in
which application-defined graphics can be drawn using Common Graphics
operations such as fillRectangle:, drawArc:, and drawString:. Consult the Common
Graphics chapter for an explanation of drawing and other graphics operations.

Drawing is actually done on the CgWindow associated with the CwDrawingArea.


Every CwWidget has a corresponding CgWindow, obtained by sending window to a
widget, that can be used for drawing. Although any widget can be drawn on in
this manner, CwDrawingArea widgets are typically used because they provide
additional drawing-related functionality. Create CwDrawingArea widgets using the
createDrawingArea:argBlock: convenience method.

A CwDrawingArea can be told to notify the application with an expose callback


whenever a part of the drawing area needs to be redrawn. The expose callback
contains an expose event with a rectangle describing the damaged area of the
widgets CgWindow.

The following example is a simple application that draws a mandala. (A mandala


is a drawing of lines connecting each of a given number of points on the
circumference of a circle to every other such point.) Four callbacks that are often
used in conjunction with drawing areas are illustrated: exposeCallback (described
above), resizeCallback, inputCallback, and destroyCallback.

The resize callback is called when the drawing area changes size, usually due to a
change in the size of a parent widget. If an expose callback is triggered as a result
of a resize, the resize callback is always sent before the expose callback. It is
possible for the resize callback to be run before the window has been realized. The
resize callback handler should handle the case where the window message returns
nil.

The input callback is called when a mouse button is pressed or released inside the
widget or a key on the keyboard has been pressed or released. The destroy
callback, run when the widget is about to be destroyed, is a good place to free any
graphics resources that have been allocated for drawing.
Object subclass: #DrawingAreaExample
instanceVariableNames: 'gc radius segments '
classVariableNames: ''
poolDictionaries: 'CwConstants CgConstants '

example1
"Open the drawing area example."

| diameter shell draw |


"Initialize the radius instance variable and calculate the diameter
of the mandala."
radius := 150.
diameter := radius * 2.

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Drawing Area Example'].
draw := shell
"Create a drawing area widget, with its width and height set to the
diameter of the mandala"
createDrawingArea: 'draw'
argBlock: [:w |

164 IBM Smalltalk: Programmers Reference


w
width: diameter;
height: diameter].
draw manageChild.
draw
"Add an expose callback that is run when a portion of the drawing area
needs redrawing."
addCallback: XmNexposeCallback
receiver: self
selector: #expose:clientData:callData:
clientData: nil;
"Add a resize callback that is run when the drawing area is resized
as a result of the user resizing the shell."
addCallback: XmNresizeCallback
receiver: self
selector: #resize:clientData:callData:
clientData: nil;
"Add an input callback that is run when the drawing area receives a
key or mouse button event."
addCallback: XmNinputCallback
receiver: self
selector: #input:clientData:callData:
clientData: nil;

"Add a destroy callback that is run when the drawing area is destroyed.
The destroy callback is a good place to free any allocated graphics
resources."
addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.

"Realize the widgets".


shell realizeWidget.

expose: widget clientData: clientData callData: callData


"Redraw the contents of the drawing area."

gc isNil ifTrue : [
gc := widget window
"On the first expose, create a graphics context with a foreground color
of black."
createGC: None
values: nil.
gc setForeground: widget window blackPixel].

callData event count = 0


ifTrue: [
segments isNil
ifTrue: [self recalculateSegments: widget].

widget window
"Draw the line segments."
drawSegments: gc
segments: segments].
recalculateSegments: widget
"Recalculate the coordinates of the mandala's line segments."
| n points x y |

n := 20.

Chapter 7. Common Widgets 165


points := OrderedCollection new.
"Calculate the points of the mandala."
0 to: Float pi * 2 by: Float pi * 2 / n do: [:angle |
x := (angle cos * radius) rounded + (widget width // 2).
y := (angle sin * radius) rounded + (widget height // 2).
points add: x@y].

segments := OrderedCollection new.


"Calculate the line segments of the mandala.
Each point is connected to every other point."
1 to: points size - 1 do: [:i |
i + 1 to: points size do: [:j |
segments add:
(CgSegment
point1: (points at: i)
point2: (points at: j))]].
resize: widget clientData: clientData callData: callData
"The drawing area has been resized."
widget window notNil
ifTrue: [
"Recalculate the radius of the mandala. Force a recalculation of
segments on expose."
radius := (widget width min: widget height) // 2.
segments := nil].
input: widget clientData: clientData callData: callData
"The drawing area has received an input callback (button or key event).
Explicitly destroy the widget if one of three things has happened:
- the user typed 'Q' or 'q'.
- the user typed 'control-DownArrow'.
- the user did a 'shift-click' (shift key pressed, click left
mouse button)."
| event quit |

quit := false.
event := callData event.

"$Q, $q, or control-End typed"


event type = KeyPress
ifTrue: [
quit := ('Qq' includes: event character)
or: [(event state & ControlMask) = ControlMask
and: [event keysym = XKdownarrow]]].

"Shift-click"
(event type = ButtonPress and: [event button = 1])
ifTrue: [
quit := (event state & ShiftMask) = ShiftMask].

quit ifTrue: [widget shell destroyWidget].


destroy: widget clientData: clientData callData: callData
"The drawing area has been destroyed.
Free any allocated graphics resources."
gc notNil ifTrue: [
"Free the graphics context."
gc freeGC].

Adding an event handler to a drawing area


In the following example, a button press event handler is used to detect
double-clicks in a drawing area. The open method creates the widgets, and the
buttonPress:clientData:event: method handles button press events.

166 IBM Smalltalk: Programmers Reference


Object subclass: #DoubleClick
instanceVariableNames: 'clickStartTime '
classVariableNames: ''
poolDictionaries: 'CgConstants CwConstants '
open
"Create a drawing area inside a shell."
| shell drawingArea |

clickStartTime := 0.
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Double-click test'].
(drawingArea := shell
createDrawingArea: 'draw'
argBlock: [:w |
w
width: 100;
height: 100])
manageChild.
drawingArea
addEventHandler: ButtonPressMask
receiver: self
selector: #buttonPress:clientData:event:
clientData: nil.
shell realizeWidget.
buttonPress: widget clientData: clientData event: event
"Detect double click by checking whether the time between successive
presses of the left mouse button is less than the system-defined
double-click time."
event button = 1
ifTrue: [
event time - clickStartTime < widget display doubleClickInterval
ifTrue: [
clickStartTime := 0.
Transcript cr; show: 'DOUBLE CLICK' ]
ifFalse: [
clickStartTime := event time ]].

Tip: Adding a mouse down event handler to a widget that processes mouse events
internally, such as a CwPushButton, might result in unpredictable behaviour.
To detect double-clicks in a CwList, use the defaultAction callback.

Layout widgets
The form (CwForm) and row-column (CwRowColumn) widgets are composite
widgets that allow the application to specify how child widgets of the composite
should be laid out relative to each other and relative to the composite.

Form widgets
Create form widgets using the createForm:argBlock: convenience method. Position
form widget children by attaching their sides to other objects. Specify attachments
by setting each childs leftAttachment, rightAttachment, topAttachment and
bottomAttachment resources. A side can be attached either to a given position, to
another widget, or to the edge of the form. The attachment types are listed below.
The first four types are the most commonly used. All are described in terms of the
leftAttachment, but the same attachment types apply to the other sides, with
corresponding behavior.
XmATTACHNONE
Default. Do not attach this side.

Chapter 7. Common Widgets 167


XmATTACHFORM
Attach the left side of the child to the left side of the form.
XmATTACHWIDGET
Attach the left side of the child to the right side of the widget specified in
the leftWidget resource.
XmATTACHPOSITION
Attach the left side of the child to a relative position in the form. This
position is specified by the leftPosition resource, and is a fractional value of
the width of the form, with the default range being from 0 to 100. The
position is relative to the left side of the form for left and right attachments,
and to the top of the form for top and bottom attachments. A position of 0
places the left side of the child at the left side of the form. A position of
100 places the left side of the child at the right side of the form.
XmATTACHOPPOSITEFORM
Attach the left side of the child to the right side of the form.
XmATTACHOPPOSITEWIDGET
Attach the left side of the child to the left side of the widget specified in
the leftWidget resource.
XmATTACHSELF
Attach the left side of the child to its initial position in the form.

Tip: It is an error for attachments to be recursively defined. For example, if a


widget A is attached to a widget B, then widget B. cannot be attached to
widget A. More generally, there must not be a cycle in the widget
attachments.

If the attachment is XmATTACHFORM or XmATTACHWIDGET, an offset can also


be specified that adds space between the side of the widget and the object to
which it is attached. Offsets are specified by the leftOffset, rightOffset, topOffset, and
bottomOffset resources. Offsets are specified in units of pixels.

Tip: The results are undefined if an offset setting is used with an attachment type
of XmATTACHPOSITION.

If attachments have been set on all sides of a widget, the size of the widget is
completely determined by the form and the other child widgets. However, if a side
is left unattached, the widget will use its preferred size in the corresponding
dimension. This is useful for allowing widgets to size themselves automatically
based on their font size, contents, and other attributes.

Some convenience methods, such as those used to create a scrolled list or a scrolled
text, actually create a widget subtree, but instead of returning the root of the
subtree, the child is returned. In these cases, the form attachments must be set on
the returned widgets parent, rather than on the widget itself. For an example, see
Scrolled lists on page 188.

The following diagram illustrates a form containing a drawing area and a text
widget. The right side of the drawing area is attached to a position two-thirds (67
per cent) of the way from left to right. The left side of the text widget is attached
to the right side of the drawing area. The remaining sides of the text and drawing
area widgets are attached to the form. The widgets are offset from each other by
two pixels. (Offsets in the diagram have been exaggerated to show the

168 IBM Smalltalk: Programmers Reference


attachments.)

Form Example
Shell widget
To form To form

Drawing area widget Text widget


Form widget

To form To position To form

To drawing area

To form To form

The following code example creates the widget tree illustrated above:
| shell form drawing text |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Form Example'].

form := shell
createForm: 'form'
argBlock: nil.
form manageChild.
drawing := form
createDrawingArea: 'drawing'
argBlock: [:w |
w
borderWidth: 1;
width: 200;
height: 200;
leftAttachment: XmATTACHFORM;
leftOffset: 2;
rightAttachment: XmATTACHPOSITION;
rightPosition: 67;
topAttachment: XmATTACHFORM;
topOffset: 2;
bottomAttachment: XmATTACHFORM;
bottomOffset: 2].
drawing manageChild.
text := form
createText: 'text'
argBlock: [:w |
w
leftAttachment: XmATTACHWIDGET;
leftWidget: drawing;
leftOffset: 2;
rightAttachment: XmATTACHFORM;
rightOffset: 2;
topAttachment: XmATTACHFORM;
topOffset: 2;
bottomAttachment: XmATTACHFORM;
bottomOffset: 2].
text manageChild.
shell realizeWidget.

Row-column widgets
The row-column widget (CwRowColumn) positions its children in rows or columns.
CwRowColumn widgets are frequently used to lay out groups of buttons, including

Chapter 7. Common Widgets 169


pop-up and pull-down menus. You can also use them to lay out widgets in a table.
Create row-column widgets using the createRowColumn:argBlock: convenience
method.

Some commonly used row-column resources are the orientation, marginWidth,


marginHeight and spacing resources. The orientation resource specifies that the layout
is either row major or column major. In a column major layout, specified by
XmVERTICAL, the children are laid out in columns top to bottom. In a row major
layout, specified by XmHORIZONTAL, the children are laid out in rows. The
default orientation is XmVERTICAL. The marginWidth and marginHeight resources
specify the size of the margin between the child widgets and the edges of the
row-column. The spacing resource specifies the spacing between child widgets.

In the following illustration, the buttons on the left are organized in a row-column
widget. The row-column and the drawing area are contained in a form, similar to
the previous example.

RowColumn Example

Kitchen

Dining Room

Living Room

Washroom

Bedroom

Workshop

The following code creates the example shown above:


| shell form rowColumn drawing |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'RowColumn Example'].

form := shell
createForm: 'form'
argBlock: nil.
form manageChild.
rowColumn := form
createRowColumn: 'rooms'
argBlock: [:w |
w
orientation: XmVERTICAL;
marginWidth: 10;
marginHeight: 10;
spacing: 20;
leftAttachment: XmATTACHFORM;
topAttachment: XmATTACHFORM;
bottomAttachment: XmATTACHFORM].
rowColumn manageChild.
#('Kitchen' 'Dining Room' 'Living Room' 'Washroom' 'Bedroom' 'Workshop')
do: [:room |
(rowColumn
createPushButton: room
argBlock: nil)
manageChild].

170 IBM Smalltalk: Programmers Reference


drawing := form
createDrawingArea: 'drawing'
argBlock: [:w |
w
borderWidth: 1;
width: 300;
leftAttachment: XmATTACHWIDGET;
leftWidget: rowColumn;
leftOffset: 2;
rightAttachment: XmATTACHFORM;
rightOffset: 2;
topAttachment: XmATTACHFORM;
topOffset: 2;
bottomAttachment: XmATTACHFORM;
bottomOffset: 2].
drawing manageChild.
shell realizeWidget.

Button and label widgets


The Common Widgets subsystem allows applications to create static text labels
(CwLabel) and several types of buttons:
v Push buttons (CwPushButton)
v Cascade buttons (CwCascadeButton)
v On/off toggle buttons (CwToggleButton)
v Application-drawn buttons (CwDrawnButton)

Buttons and labels can display either strings, pixmaps or icons as their contents,
depending on the value of the labelType resource. See Icon and pixmap label and
button widgets on page 176 for more detail on labelType.

The following resources define the visual appearance of labels and buttons: x, y,
height, width, marginTop, marginBottom, marginHeight, marginWidth, marginLeft, and
marginRight. These are illustrated in the following figure:

(x,y) width
marginHeight
marginTop
height

Widget Label
marginBottom
marginHeight
marginLeft

marginRight
marginWidth

marginWidth

The marginTop, marginBottom, marginRight, and marginLeft resources are typically


controlled by subclasses of CwLabel or by the labels parent. For example, a
CwToggleButton could increase marginRight to make space for the toggle indicator.
The marginHeight and marginWidth resources are usually left alone by subclasses,
and can be manipulated by the application if desired.

Tip: The margin resource settings indicate the preferred appearance of the widget.
They might be ignored if they are not supported by the platform or conflict
with the platforms look and feel.
By default, the name given in a label or button creation message is used as the
widgets labelString. The contents of a label or button widget are changed using the
labelString: resource method.

Chapter 7. Common Widgets 171


CwLabel provides accelerator and acceleratorText resources for adding an accelerator
key to a toggle button or push button that is in a pop-up or pull-down menu. An
accelerator key will activate a button at any time, provided the parent menu is
managed. The accelerator resource is set to an instance of CwAccelerator created
using the mask:keysym: class method, which takes the following arguments:
mask The modifier key mask, which consists of a logical-OR of zero or more of
the following: Mod1Mask, ControlMask, or ShiftMask.
keysym
The unmodified key, which must be a lowercase letter or special key,
represented by a CwConstants XK keysym value.

The acceleratorText resource describes the string that is displayed beside the button
in the menu.

Note: On some platforms, accelerators are case sensitive. A keysym value of XKn
only fires with a lowercase accelerator key press.

Static label widgets


Create static label widgets (CwLabel) using the createLabel:argBlock: convenience
method. Static labels do not provide any special callbacks. The following code
creates the example to its right.

| shell label |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Label Example']. This is a label.
label := shell
createLabel: 'label'
argBlock: nil.

label labelString: 'This is a label.'.


label manageChild.
shell realizeWidget.

Push-button widgets
Create push-button widgets (CwPushButton) using the createPushButton:argBlock:
convenience method.

Push
Push buttons call their activate callback when they are pressed and released.

In the following example, three buttons are created in a row-column. An activate


callback has been added to each button. The same callback message is used in all
three cases. The client data of the callback is used to identify which button was
pressed. For simplicity, the shell is not shown in the diagram.

| shell rowColumn row b1 b2 b3 |


shell := CwTopLevelShell
Top
createApplicationShell: 'Test'
argBlock: nil. Middle
rowColumn := shell
createRowColumn: 'buttons' Bottom
argBlock: nil.
rowColumn manageChild.

172 IBM Smalltalk: Programmers Reference


b1 := rowColumn
createPushButton: 'Top'
argBlock: nil.
b1
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: 'top'.
b1 manageChild.
b2 := rowColumn
createPushButton: 'Middle'
argBlock: nil.
b2
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: 'middle'.
b2 manageChild.
b3 := rowColumn
createPushButton: 'Bottom'
argBlock: nil.
b3
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: 'bottom'.
b3 manageChild.
shell realizeWidget.

Here is the activate callback used in the code:


button: widget clientData: clientData callData: callData
"A button has been pressed."
Transcript cr; show: 'The ', clientData, ' button has been pressed.'

Toggle-button widgets
Create toggle-button widgets (CwToggleButton) using the createToggleButton:argBlock:
convenience method.

Toggle buttons have two states: on and off. The state of a toggle button can be
queried and changed using the getState and setState:notify: messages, respectively.
Toggle buttons call their valueChanged callback when their state is changed.

Toggle buttons are typically used to create radio-button and check-box groups
using row column convenience methods described in the next sections. The
toggle-button indicatorType resource controls whether the toggle button has a
radio-button or a check-box appearance. When the resource value is set to
XmONEOFMANY, the button has a radio-button appearance. When the value is set
to XmNOFMANY, the button has a check-box appearance.

Tip: On some platforms, toggle buttons turn on when they are given focus.

Chapter 7. Common Widgets 173


Radio-button groups
A row-column widget containing several toggle-button widgets (CwToggleButton)
can be configured to have radio-button behavior.

Hello
Bonjour
Hola
When a button is selected in this mode, any other selected buttons in the group are
automatically deselected, leaving only one button selected at any time. The
radioBehavior resource of the CwRowColumn widget controls this behavior.

Create a CwRowColumn with radioBehaviour set to true using the convenience


method createRadioBox:argBlock:.

Tip: As a side effect of createRadioBox:argBlock:, the CwRowColumn isHomogeneous


resource is set to true. Children of a homogeneous row-column widget must
all be of the same type. In this case, they must all be CwToggleButton widgets.
You can select or deselect a toggle button using the setState:notify: method. Its state
can be queried using the getState method. The valueChanged callback of a toggle
button is run whenever the state of the button changes.

Tip: The valueChanged callback is run when a button is deselected as well as when
it is selected. The state of the widget should be checked in the callback using
the getState method, or by checking the set field of the callback data.

In the following example, a radio-box row-column is created. Three toggle buttons


are added. The same valueChanged callback is added to each toggle button, with
the client data used to identify the selected button. The resulting radio-button
group is shown in the left margin. For simplicity, the shell is not shown.
| shell rowColumn button buttonNames initialValues languageNames |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Radio Box Example'].
rowColumn := shell
createRadioBox: 'radio'
argBlock: nil.
rowColumn manageChild.
buttonNames := #('Hello' 'Bonjour' 'Hola').
initialValues := (Array with: true with: false with: false).
languageNames := #('English' 'French' 'Spanish').

1 to: buttonNames size


do: [:i |
button := rowColumn
"The state of each toggle button is set on creation according
to the corresponding value in the initialValues array. In this
example, Hello is selected."
createToggleButton: (buttonNames at: i)
argBlock: [:w | w set:
(initialValues at: i)].
button
addCallback: XmNvalueChangedCallback
receiver: self
selector: #language:clientData:callData:
clientData: (languageNames at: i).
button manageChild].

174 IBM Smalltalk: Programmers Reference


shell realizeWidget.

The valueChanged callback used by the code is shown below. The selected
language is indicated by the clientData argument. A message is written to the
transcript whenever a new language is chosen.
language: widget clientData:clientData callData: callData
"A toggle button has changed state."
callData set
ifTrue: [Transcript cr; show: 'The selected language is now ',
clientData, '.'].

Check boxes
Check boxes consist of several toggle buttons that present a set of options to the
user. The user can choose none, all, or any combination of the buttons. A
CwRowColumn widget can be used to contain the buttons. When the row-columns
radioBehavior resource is false, its default, more than one toggle button can be
selected at a time.

The code that follows creates the toggle-button group shown on the right.

| shell rowColumn button buttonNames initialValues |


shell := CwTopLevelShell Item1
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Check Box Example']. Item2
rowColumn := shell Item3
createRowColumn: 'group'
argBlock: nil.
rowColumn manageChild.

buttonNames := #('Item 1' 'Item 2' 'Item 3').


initialValues := (Array with: false with: true with: true).
1 to: buttonNames size
do: [:i |
button := rowColumn
createToggleButton: (buttonNames at: i)
"The state of each toggle button is set on creation according to the
corresponding value in the initialValues array. In this example, Item2
and Item3 are both selected, and this is indicated by an X."
argBlock: [:w | w set: (initialValues at: i)].
button
addCallback: XmNvalueChangedCallback
receiver: self
selector: #valueChanged:clientData:callData:
clientData: nil.
button manageChild].
shell realizeWidget.

The valueChanged callback used by the code is shown below. A message is written
to the transcript whenever a button is selected or deselected.
valueChanged: widget clientData:clientData callData: callData
"A toggle button has changed state."
Transcript cr; show: widget labelString, ' has been '.
callData set
ifTrue: [Transcript show: 'selected.']
ifFalse: [Transcript show: 'deselected.'].

Chapter 7. Common Widgets 175


Icon and pixmap label and button widgets
The contents of CwLabel, CwPushButton,CwCascadeButton, and CwToggleButton
widgets can be a string, an icon or a pixmap.

?
When the labelType resource is XmSTRING, the labelString resource specifies the
string to display. The default type is string. When labelType is XmICON, labelIcon
specifies the icon to use, and when labelType is XmPIXMAP, labelPixmap specifies
the pixmap to display. Consult Using pixmaps on page 106 for more information
on using pixmaps and icons.

The code below creates a widget tree containing a pixmap button. Icon buttons
and labels are created in a similar manner. Note that pixmap is an instance variable.
| shell button questionMark pixmap |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Pixmap Button Example'].

button := shell
"Create the push button."
createPushButton: 'button'
argBlock: nil.

button
addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.
questionMark :=
"Initialize the data for the pixmap."
#(0 0 0 0 255 0 192 255 3 224 255 7
240 255 15 248 255 31 248 255 31 252 255 63
252 227 63 252 193 63 252 193 63 252 193 63
248 224 63 248 240 31 0 248 31 0 252 15
0 252 7 0 254 3 0 254 3 0 254 1
0 254 1 0 252 0 0 252 0 0 0 0
0 252 0 0 254 1 0 254 1 0 254 1
0 254 1 0 252 0 0 0 0 0 0 0).

"Realize the shell without mapping it so we have access to the


button's window and palette without making the shell appear."
shell
mappedWhenManaged: false;
realizeWidget.
pixmap := button screen rootWindow
"Create the pixmap. Note that the background color of the pixmap will be
the same as the button's background color."
createPixmapFromBitmapData: questionMark
width: 24
height: 32
fg: button window blackPixel
bg: (button window getPalette
nearestPixelValue: button backgroundColor)
depth: button depth.

button
setValuesBlock: [:w |
w

176 IBM Smalltalk: Programmers Reference


"Complete the button's initialization."
labelType: XmPIXMAP;
labelPixmap: pixmap].
button manageChild.

shell mapWidget.

destroy: widget clientData: clientData callData: callData

pixmap freePixmap.

Application-drawn buttons
Application-drawn button widgets (CwDrawnButton) enable the application to
draw arbitrary graphics on a button. Drawn buttons behave like push buttons
except that they can be drawn on like drawing area widgets. See the example
below.

As with the push-button widget, the application can add an activate callback to be
run when the button is pressed. As with the drawing area widget, expose and resize
callbacks can be added to notify the application when the button requires
redrawing and when it has changed size. Consult Drawing operations on page
89 chapter for more information on drawing graphics.

In the code below, a drawn button is created and drawn.


Object subclass: #DrawnButtonExample
instanceVariableNames: 'gc '
classVariableNames: ''
poolDictionaries: 'CwConstants CgConstants '
open
| shell button |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Drawn Button Example'].
button := shell
createDrawnButton: 'button'
argBlock: nil.
button
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: nil;
addCallback: XmNexposeCallback
receiver: self
selector: #expose:clientData:callData:
clientData: nil;
addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.
button manageChild.
shell realizeWidget.

Chapter 7. Common Widgets 177


gc := button window
createGC: None
values: nil.
activate: widget clientData: clientData callData: callData
"The drawn button has been pressed."
Transcript cr; show: 'The pixmap button has been pressed.'.
expose: widget clientData: clientData callData: callData
"The drawn button has been exposed. Redraw the button."
| x |

callData event count = 0


ifTrue: [
0 to: 10 do: [:i |
x := widget width * i // 10.
widget window
drawLine: gc
x1: x
y1: 0
x2: widget width - x
y2: widget height - 1]].
destroy: widget clientData: clientData callData: callData
gc freeGC.

Menus
Menus are used extensively in most graphical user interfaces. Common Widgets
provides protocol for pop-up menus, option menus and menu bars containing
pull-down menus. Menu bars and menus are built using CwRowColumn widgets.
Items in a menu bar are cascade buttons, which are buttons that drop down a
menu when selected. Pop-up menus and pull-down menus within a menu bar are
also represented by CwRowColumn widgets. Items within a menu are separators,
labels, push buttons, toggle buttons, or cascade buttons for submenus. There are
two ways to create menus: using simple menu convenience methods and using
non-simple widget creation methods.

Greying out buttons


When you create buttons, they are active by default. You can set the state of
buttons (sensitive or not sensitive) using the method setSensitive: or sensitive:. If the
argument is true, then the button will be active. If the argument is false, then the
button will be deactivated (greyed out).

You can query the state of a button by sending it the message sensitive.

Note that any button can be deactivated, including menu buttons.

The following example creates two buttons, then deactivates one of them. You can
execute this example in a Workspace or the Transcript.
| shell button1 button2 rowColumn |
shell := CwTopLevelShell
createApplicationShell: 'Shell'
argBlock: nil.

rowColumn := shell
createRowColumn: 'buttons'
argBlock: nil.
rowColumn manageChild.

button1 := rowColumn

178 IBM Smalltalk: Programmers Reference


createPushButton: 'One'
argBlock: nil.
button1 manageChild.

button2 := rowColumn
createPushButton: 'Two'
argBlock: nil.
button2 manageChild.

shell realizeWidget.
button2 sensitive: false.

Simple menus and menu bars


Simple menu convenience methods create row-columns which are configured as
simple menu bars or simple menus. These convenience methods are the preferred
method of creating a menu system for the following reasons:
v Simple menus are easier to program
v Simple menus use fewer operating system resources on some platforms
v Simple menus are created faster on some platforms

Create a simple menu or menu bar using one of the following simple menu
convenience methods:
createSimpleMenuBar:argBlock:
Creates a simple menu bar
createSimplePulldownMenu:argBlock:
Creates a simple pull-down menu
createSimplePopupMenu:argBlock:
Creates a simple pop-up menu

The menu or menu bar is entirely defined by the creation message and resources
set in the argBlock. The resources that can be set to define a simple menu or
simple menu bar are listed in the following table. The first three resources are
arrays whose elements each represent a single menu bar item. These arrays must
contain the same number of items, and must describe corresponding items in the
same sequence, in order for the menu or menu bar to be correctly defined.
Table 37. Simple Menu and Simple Menu Bar Resources
Resource Name Description Value(s)
buttons An array of strings specifying the String
item labels. v String is ignored for
separators, but must be
present.

Chapter 7. Common Widgets 179


Table 37. Simple Menu and Simple Menu Bar Resources (continued)
Resource Name Description Value(s)
buttonType An array of constants specifying XmCASCADEBUTTON
the item types. v A cascade button for a
submenu
v Default type for menu bar
XmPUSHBUTTON
v A normal menu item
v Default type for menu
XmSEPARATOR
v A line separating menu items
XmDOUBLESEPARATOR
v A double line separating items
XmRADIOBUTTON
v A radio-button menu item
XmCHECKBUTTON
v A check-button menu item
XmTITLE
v A title menu item
buttonMnemonics An array of characters specifying Character
the item mnemonics. v Character is ignored if it is not
in the corresponding item
string. The code 0
asCharacter is useful for
specifying no mnemonic.
postFromButton Only used for pull-down menu Integer
creation. An integer specifying
the zero-based index of the
cascade button specified as the
parent of the pull-down menu.

Row-columns configured as simple menus or menu bars provide a simple callback


that is run whenever any item in a menu is selected. The clientData argument of
the callback method is an integer indicating the zero-based position of the selected
button in the menu (separators are ignored when determining the button
positions).

Tip: When you add a simple callback to a row-column configured as a simple


menu or simple menu bar, the clientData argument is ignored.

Creating a menu bar and pull-down menu using simple menu


protocol
The following code creates a window with a menu bar containing the menu
illustrated at right. Simple menu creation methods are used.

| shell main menuBar fileMenu |


shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w |
w title: 'Pull-down Menu Example'].

180 IBM Smalltalk: Programmers Reference


main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.
"A simple menu bar is created as a child of the main window.
The label for the button and its mnemonic is specified. In a
menu bar, all items are cascade buttons, so the buttonType
resource need not be explicitly set."
menuBar := main
createSimpleMenuBar: 'bar'
argBlock: [:w | w
buttons: #('File');
buttonMnemonics: #($F)].
menuBar manageChild.
fileMenu := menuBar
"A simple pull-down menu is created as a child of the menu bar."
createSimplePulldownMenu: 'file'
argBlock: [:w |
w
buttons: #('Open' 'separator' 'Close');
buttonType: (Array
"Two normal items and a separator are specified"
with: XmPUSHBUTTON
with: XmSEPARATOR
with: XmPUSHBUTTON);
buttonMnemonics: (Array
with: $O
with: 0 asCharacter
with: $C);
"The menu is attached to (dropped down from) the first
(0th) item i the menu bar: the File cascade button."
postFromButton: 0].
fileMenu
"A simple callback runs whenever an item is selected from the File menu."
addCallback: XmNsimpleCallback
receiver: self
selector: #fileMenu:clientData:callData:
clientData: nil.
main
setAreas: menuBar
horizontalScrollbar: nil
verticalScrollbar: nil
workRegion: nil.

shell realizeWidget.
fileMenu: widget clientData: clientData callData: callData
"Execute the desired operation."

self perform: (#(#doOpen #doClose) at: clientData + 1).

Creating a secondary menu using simple menu protocol


The following code creates a window with a menu bar containing the menu and
secondary menu illustrated at right. The menu bar contains one item, File, that
drops down a menu containing two items, Open and Close. If Close is selected, the
fileMenu:clientData:callData: method is invoked. If Open is selected, a secondary
menu containing three items is dropped down. Choosing one of the three items
invokes the openMenu:clientData:callData: method.

Chapter 7. Common Widgets 181


| shell main menuBar fileMenu openMenu |

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w |
w title: 'Secondary Menu Example'].

main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.
menuBar := main
createSimpleMenuBar: 'bar'
argBlock: [:w |
w
"File defaults to a cascade button because it is in a menu bar."
buttons: #('File');
buttonMnemonics: #($F)].
menuBar manageChild.
fileMenu := menuBar
createSimplePulldownMenu: 'file'
argBlock: [:w |
w
buttons: #('Open' 'separator' 'Close');
"Open must be explicitly created as a cascade button, because
buttons in a menu default to push buttons."
buttonType: (Array
with: XmCASCADEBUTTON
with: XmSEPARATOR
with: XmPUSHBUTTON);
buttonMnemonics: (Array
with: $O
with: 0 asCharacter
with: $C);
postFromButton: 0].
fileMenu
addCallback: XmNsimpleCallback
receiver: self
selector: #fileMenu:clientData:callData:
clientData: nil.
openMenu := fileMenu
"The secondary pull-down menu is created as a child of fileMenu."
createSimplePulldownMenu: 'open'
argBlock: [:w |
w
buttons: #('Read only' 'Write only' 'Read write');
"The secondary menu is activated when the 0th item from fileMenu is selected
(Open)."
postFromButton: 0].
openMenu
addCallback: XmNsimpleCallback
receiver: self
selector: #openMenu:clientData:callData:
clientData: nil.
main
setAreas: menuBar
horizontalScrollbar: nil
verticalScrollbar: nil
workRegion: nil.

shell realizeWidget

182 IBM Smalltalk: Programmers Reference


fileMenu: widget clientData: clientData callData: callData
"Execute the desired operation."
self perform: (#(#doNothing #doClose) at: clientData + 1).

openMenu: widget clientData: clientData callData: callData


"Execute the desired operation."
self
perform: (#(#doOpenReadOnly #doOpenWriteOnly #doOpenReadWrite)
at: clientData + 1).

Creating a pop-up menu using simple menu protocol


A pop-up menu is independent of a menu bar. It is popped up in response to a
mouse button-3 press, or release, depending on the platform. In order for the
application to be notified of the mouse button event, an event handler must be
added to the appropriate widget.

Tip: Some platforms cause pop-up menus to appear on a mouse up event, and
some on a mouse down event. The correct event for the platform must be
used, or unpredictable behavior might result. To ensure that the correct event
is used, specify ButtonMenuMask as the event mask when adding the event
handler.

The open method of the class below opens a window containing a label widget.
When a menu is requested over the label, a pop-up menu (shown at right)
appears. The pop-up menu is created using a simple menu creation method. The
menu is popped up by managing the row-column widget that represents the
menu. It is automatically unmanaged when the menu is canceled by the user.

"This class illustrates the use of pop-up menus. Note that


the pop-up menu is kept in an instance variable so that it
can be referenced by the button event handler."
Object subclass: #PopupMenuExample
instanceVariableNames: 'menu '
classVariableNames: ''
poolDictionaries: 'CwConstants '

open
"Open the pop-up menu example."
| shell label |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Popup Menu Example'].
label:= shell
createLabel: 'main'
argBlock: [:w | w labelString:
'Push the menu button over this message'].
label
"An event handler is added to the label widget. ButtonMenuMask label
indicates that the event handler should be activate when the menu
button on the mouse is pressed or released."
addEventHandler: ButtonMenuMask
receiver: self
selector: #button:clientData:event:
clientData: nil.
label manageChild.
"The pop-up menu is created. Note that the menu is not managed at this
stage. It will be managed when the event handler pops the menu up."
menu := label
createSimplePopupMenu: 'menu'

Chapter 7. Common Widgets 183


argBlock: [:w |
w
buttons: #('Add' 'Change' 'Delete')].
"Three menu items are specified. A callback is registered,
to be run when an item is selected."
menu
addCallback: XmNsimpleCallback
receiver: self
selector: #menuItem:clientData:callData:
clientData: nil.
"When the shell is realized, the shell and the label widgets
appear, but the menu does not, because it is not managed."
shell realizeWidget
button: widget clientData: clientData event: event
"Handle a menu button press event. The call data event structure is
checked for a mouse-button press. If it was not the menu button,
the menu is not popped up."
event button = 3 ifFalse: [|self].
"Position and pop up the menu by managing it."
menu
"The menu is positioned using the event object. It is popped up by
managing it."
menuPosition: event;
manageChild
menuItem: widget clientData: clientData callData: callData
"Display the index of the selected menu item in the transcript."
Transcript cr; show: 'Menu item ', clientData printString,
' was selected from the pop-up menu.'

Non-simple menus and menu bars


The other way to create a menu or menu bar is to create a CwRowColumn widget
configured as a menu or menu bar, and add individual button and separator
widgets for the menu items. Convenience methods are provided for creating
CwRowColumn widgets that behave as menu bars or as pop-up and pull-down
menus. Although non-simple menus allow separate callbacks to be registered for
each menu item, they are more cumbersome to program than simple menus.

Non-simple menu example


The following code creates a window with a menu bar containing the same File
menu illustrated in Creating a menu bar and pull-down menu using simple menu
protocol on page 180, except that this time, non-simple widget creation methods
are used.
| shell main menuBar menu1 menu1Title item1 item2 item3 |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Pull-down Menu Example'].

main := shell
createMainWindow: 'main'
argBlock: nil.
main manageChild.
menuBar := main
"First, a menu bar is created as a child of the main window."
createMenuBar: 'bar'
argBlock: nil.
menuBar manageChild.

184 IBM Smalltalk: Programmers Reference


menu1 := menuBar
"Second, a pull-down menu is created as a child of the menu bar."
createPulldownMenu: 'menu'
argBlock: nil.
menu1Title := menuBar
"Third, a cascade button is added to the menu bar. It is associated
with the pull-down menu using the subMenuId: method. The name of
the cascade button appears in the menu bar. Note that the pull-down
menu is created before the cascade button."
createCascadeButton: 'File'
argBlock: [:w |
w
subMenuId: menu1].
menu1Title manageChild.
item1 := menu1
"Fourth, a push-button widget is created. The parent is menu1.
When the File menu is clicked on, a pull-down menu appears
with Open as the first entry. If Open is selected, an
XmNactivateCallback is issued and the message
open:clientData:callData is sent to the receiver."
createPushButton: 'Open'
argBlock: nil.
item1
addCallback: XmNactivateCallback
receiver: self
selector: #open:clientData:callData:
clientData: nil.
item1 manageChild.
item2 := menu1
"Fifth, a separator widget is created as a child of menu1,
to separate the first and third menu items. The separatorType
resource is set to a single line separator."
createSeparator: 'sep'
argBlock: [:w |
w
separatorType: XmSINGLELINE].
item2 manageChild.
item3 := menu1
"Finally, another push-button widget is created for the third menu item.
If Close is selected, an XmNactivateCallback is issued and the message
close:clientData:callData: is sent to the receiver."
createPushButton: 'Close'
argBlock: nil.
item3
addCallback: XmNactivateCallback
receiver: self
selector: #close:clientData:callData:
clientData: nil.
item3 manageChild.

shell realizeWidget.

List widgets
List widgets (CwList) present a list of items and allow the user to select one or
more items from the list. List widgets are created using thecreateList:argBlock:and
createScrolledList:argBlock: convenience methods. The latter method makes the list
scrollable, but otherwise provides basically the same functionality. Scrolled lists are
discussed on Scrolled lists on page 188.

The items in the list and the selected items are specified by the items and
selectedItems resources, respectively. The selectionPolicy resource specifies the policy
for selecting items. It has four possible settings:

Chapter 7. Common Widgets 185


XmBROWSESELECT
Allows only single selection. Behavior might vary from platform to
platform, but normally the selection moves when the mouse is dragged.
This is the default selection policy.
XmSINGLESELECT
Allows only single selection. Behavior might vary from platform to
platform, but normally the selection remains the same when the mouse is
dragged.
XmMULTIPLESELECT
Allows multiple items to be selected. The selection of an item is toggled
when it is clicked on. Clicking on an item does not deselect previously
selected items.
XmEXTENDEDSELECT
Allows multiple items to be selected, either by dragging the selection or by
clicking on items with a modifier key held down. Behavior might vary
from platform to platform, but normally clicking on an item without a
modifier key held down deselects all previously selected items.

Tip: On some platforms, browse select and single select work the same way.

List widgets provide several methods for adding, deleting and replacing items and
selected items in the list.

The selectionPolicy resource determines which callback is used to notify the


application of changes in the selection. List widgets support the following
callbacks:
browseSelectionCallback
Executed when an item is selected in browse selection mode
singleSelectionCallback
Executed when an item is selected in single selection mode
multipleSelectionCallback
Executed when an item or group of items is selected in multiple selection
mode
extendedSelectionCallback
Executed when an item or group of items is selected in extended selection
mode
defaultActionCallback
Executed when an item is double clicked (all modes)

The call data of the selection callback specifies the item or items that were selected,
and the position(s) of the selected item(s) in the list. Item positions in the list are
numbered starting from one.

Single selection lists


In the following example, the list widget shown at right is created with its
selection policy set to XmSINGLESELECT. A single selection callback is added, to
correspond with the selection policy.

186 IBM Smalltalk: Programmers Reference


| items shell list |
items := #('item1' 'item2' 'item3' 'item4' 'item5').

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: nil.

list := shell
createList: 'list'
argBlock: [:w |
w
selectionPolicy: XmSINGLESELECT;
items: items].
list
addCallback: XmNsingleSelectionCallback
receiver: self
selector: #singleSelect:clientData:callData:
clientData: nil.
list manageChild.
shell realizeWidget.

The call data of the singleSelection callback specifies the item that was selected. The
callback method below prints the entire callback data on the transcript. All
components of the call data can be retrieved using the corresponding accessor
method.
singleSelect: widget clientData: clientData callData: callData
"Print the call data."
Transcript cr; show: 'Single selection call data: ',
callData printString

If Item 2 is selected, as in the illustration, the transcript output is as follows:


Single selection call data: CwListCallbackData(
reason -> 23
item -> 'Item 2'
itemPosition -> 2
"These three fields of the callback data are only used for multiple
and extended select lists."
selectedItems -> nil
selectedItemCount -> nil
selectedItemPositions -> nil)

Multiple selection lists


In the following example, the list widget shown at right is created with its
selection policy set to XmMULTIPLESELECT. A multiple selection callback is
added, to correspond with the selection policy.

| items shell list |


items := #('item1' 'item2' 'item3' 'item4' 'item5').

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: nil.

list := shell
createList: 'list'
argBlock: [:w |
w
selectionPolicy: XmMULTIPLESELECT;

Chapter 7. Common Widgets 187


items: items].
list
addCallback: XmNmultipleSelectionCallback
receiver: self
selector: #multipleSelect:clientData:callData:
clientData: nil.
list manageChild.
shell realizeWidget.

The call data of the multipleSelection callback specifies the items that were selected.
The callback method below prints the entire callback data on the transcript. All
components of the call data can be retrieved using the corresponding accessor
method.
multipleSelect: widget clientData: clientData callData: callData
"Print the call data."
Transcript cr; show: 'Multiple selection call data: ',
callData printString

If Item 2 and Item 3 were selected in order, as in the illustration, the transcript
output would be:
Multiple selection call data: CwListCallbackData(
reason -> 24
item -> 'Item 3'
itemPosition -> 3
selectedItems -> OrderedCollection ('Item 2' 'Item 3')
selectedItemCount -> 2
selectedItemPositions -> OrderedCollection(2 3))

Scrolled lists
A scrolled list is a CwList widget with scrolling capability. All resources and
callbacks associated with lists can be applied to scrolled lists. The scrolling
mechanism is handled automatically.

Item 1
Item 2
Item 3
Item 4
Item 5
Creating a scrolled list inserts a CwScrolledWindow parent between the list and the
receiver of the creation message. In other words, createScrolledList:argBlock: returns
an instance of CwList (the child of a CwScrolledWindow); however, the
CwScrolledWindow is the child of the form. Form attachment messages must
therefore be sent to the CwLists parent.

In the following example, a scrolled list widget is created as a child of a form. The
list selection policy is set to XmSINGLESELECT. A singleSelection callback is added,
corresponding to the selection policy.
| items shell form list |
items := OrderedCollection new.
"20 items are initialized as the list contents."
1 to: 20 do: [:i |
items add: 'Item ', i printString].
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Scrolled List Example'].

188 IBM Smalltalk: Programmers Reference


form := shell
createForm: 'form'
argBlock: nil.
form manageChild.
list := form
"Create the scrolled list. The list widget is answered, not the scrolled window."
createScrolledList: 'list'
argBlock: [:w |
w
"Set the scrolling policy to single selection, the number of visible items
to one-half of the number of items, and the list's items to items."
selectionPolicy: XmSINGLESELECT;
visibleItemCount: items size // 2;
items: items].
list
addCallback: XmNsingleSelectionCallback
receiver: self
selector: #singleSelect:clientData:callData:
clientData: nil.
list manageChild.
"Note that the attachments are set on the parent of the list (which
is a CwScrolledWindow). The scrolled window is the child of the form,
not the list widget."
list parent setValuesBlock: [:w |
w
topAttachment: XmATTACHFORM;
topOffset: 10;
bottomAttachment: XmATTACHFORM;
bottomOffset: 10;
leftAttachment: XmATTACHFORM;
leftOffset: 10;
rightAttachment: XmATTACHFORM;
rightOffset: 10].
shell realizeWidget

Tip: If other children of a form are to be attached to a scrolled list, which is a child
of the form, using XmATTACHWIDGET, they must be attached to the scrolled
lists parent.

Combo-box widgets
Like list widgets, combo-box widgets (CwComboBox) enable the user to select from
a list of available items. A combo box also displays the last selected item in a text
box above the list. Combo-box widgets can only have one item selected at a time.
Combo-box widgets are created using the createComboBox:argBlock: convenience
method.

There are two styles of combo boxes, specified by the comboBoxType resource:
XmDROPDOWN
The list is displayed only when dropped down by pressing a button beside
the text box. When a selection is made, the list disappears (default).
XmSIMPLE
The list is always displayed.

As with the list widget, the items in the combo box are specified by the items
resource. The application can add a singleSelection callback to be run whenever the
selection changes. Several methods are provided for adding, deleting, and
replacing items in the list.

Chapter 7. Common Widgets 189


The contents of the text part of the combo box can be set and retrieved using the
setString: and getString methods.

The following example creates the drop down combo box shown at right. Its items
are set, the contents of the text box are initialized to the first item, and a
singleSelection callback is added.

| items shell combo | Item 3


items := #('Item 1' 'Item 2' 'Item 3' 'Item 4').
Item 1
shell := CwTopLevelShell Item 2
createApplicationShell: 'shell' Item 3
argBlock: [:w |
w title: 'Combo Box Example']. Item 4

combo := shell
createComboBox: 'combo'
argBlock: [:w |
w
comboBoxType: XmDROPDOWN;
items: items].
combo setString: items first.
combo
addCallback: XmNsingleSelectionCallback
receiver: self
selector: #singleSelect:clientData:callData:
clientData: nil.
combo manageChild.

shell realizeWidget.

| The call data of the singleSelection callback specifies the item that was selected. The
| callback method below prints the entire callback data on the transcript. All
| components of the call data can be retrieved using the corresponding accessor
| method.
| singleSelect: widget clientData: clientData callData: callData
| "Print the call data."
| Transcript cr; show: 'Single selection call data: ',
| callData printString

| If Item 2 is selected, as in the illustration, the transcript output is as follows:


| Single selection call data: CwListCallbackData(
| reason -> 23
| item -> 'Item 2'
| itemPosition -> 2
| "These three fields of the callback data are only used for multiple
| and extended select lists."
| selectedItems -> nil
| selectedItemCount -> nil
| selectedItemPositions -> nil)

Composite-box widgets
There are two types of composite-box widgets: CwMessageBox and CwSelectionBox.
Both are subclasses of CwCompositeBox. CwCompositeBox is itself a subclass of
CwBulletinBoard, which provides useful dialog-related resources such as
autoUnmanage, dialogStyle, and dialogTitle.

190 IBM Smalltalk: Programmers Reference


MessageBox widgets
Message-box widgets (CwMessageBox) are used for common interaction tasks such
as displaying messages, asking questions and reporting errors. A message box
widget consists of a message string, an optional symbol such as an exclamation or
question mark, and three buttons. By default, the buttons are labelled OK, Cancel,
and Help, and there is no symbol displayed.

A message box is usually created in a dialog shell to notify the user of some event.
Message-box widgets are created using the createMessageBox:argBlock: and
createMessageDialog:argBlock: convenience methods. The latter method is similar to
the first, but also creates a CwDialogShell as the parent of the CwMessageBox so that
it can be popped up separately from the main widget tree. As with pop-up menus,
the message-box/dialog shell combination (message dialog) can be created and left
unmanaged until it is to be presented to the user. It is popped up by sending the
message box the manageChild message. The default behavior is to remain open until
either the OK or Cancel button is pressed, or the dialogs close box is
double-clicked. The application can explicitly close the dialog by sending
unmanageChild to the message box.

The message shown in a message box is specified by the messageString resource.


When the message box is in a dialog shell, the dialogTitle resource specifies the title
of the dialog shell. The symbol shown is specified by the dialogType resource,
which can have one of the following values:
XmDIALOGMESSAGE
Message only, no symbol (default)
XmDIALOGINFORMATION
Information symbol (an i on some plaforms)
XmDIALOGERROR
Error symbol (a stop sign on some platforms)
XmDIALOGWARNING
Warning symbol (an ! on some platforms)
XmDIALOGQUESTION
Question mark symbol
XmDIALOGWORKING
Working symbol

The following figure shows an example message dialog:

CwMessageBox Example

Warning: This is a
warning message.

OK Cancel Help

The dialogStyle resource specifies the input mode while the dialog is active, and can
have one of the following values:
XmDIALOGAPPLICATIONMODAL
Used for dialogs that must be responded to before some other interactions

Chapter 7. Common Widgets 191


in ancestors of the widget. This value is the same as
XmDIALOG_PRIMARY_APPLICATION_MODAL and remains for
compatibility.
XmDIALOGWORKAREA
Used for BulletinBoard widgets whose parents are not DialogShells. This is
the default when the parent of the BulletinBoard is not a DialogShell.
XmDIALOGMODELESS
Used for dialogs that do not interrupt interaction of any application
(default).
XmDIALOGPRIMARYAPPLICATIONMODAL
Used for dialogs that must be responded to before any other interaction in
ancestors of the widget.
XmDIALOGFULLAPPLICATIONMODAL
Used for dialogs that must be responded to before any other interaction in
the same application.
XmDIALOGSYSTEMMODAL
Used for dialogs that must be responded to before any other interaction in
any application.

If a platform does not support the specified prompter style, the style is promoted
to the next most restrictive style. If the next most restrictive style is not supported,
the style is demoted to the next less restrictive style.

The widgets comprising the message box can be retrieved using the getChild:
method. If a button or other widget is not required by the application, it can be
retrieved and unmanaged. Possible arguments for the getChild: method are:
v XmDIALOGCANCELBUTTON
v XmDIALOGDEFAULTBUTTON
v XmDIALOGHELPBUTTON
v XmDIALOGMESSAGELABEL
v XmDIALOGOKBUTTON
v XmDIALOGSEPARATOR
v XmDIALOGSYMBOLLABEL

The okCallback, cancelCallback, and helpCallback of the message box are called when
the corresponding buttons are pressed. If a callback is not added, the
corresponding button will still appear, but nothing will happen when it is pressed.
If the message box is in a dialog shell, it is unmanaged when the OK or Cancel
button is pressed, unless the autoUnmanage resource is set to false.

The following code creates an application modal information message dialog that is
popped up when a button is pressed. Only the OK button of the message dialog is
shown. The remaining ones are hidden (unmanaged). A callback is added for the
OK button. Because the dialog is application modal, the user must close the dialog
before the application can receive further input.

192 IBM Smalltalk: Programmers Reference


Object subclass: #MessageDialogExample Message Dialog
instanceVariableNames: 'messageBox '
classVariableNames: '' This is a
poolDictionaries: 'CwConstants' i message dialog.

open
| shell button | OK

shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Message Dialog Example'].
button := shell
createPushButton: 'Push me for dialog'
argBlock: nil.
button
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: nil.
button manageChild.
messageBox := shell
createMessageDialog: 'message'
argBlock: [:w |
w
dialogTitle: 'Message Dialog';
dialogStyle: XmDIALOGFULLAPPLICATIONMODAL;
messageString: 'This is a message dialog.';
dialogType: XmDIALOGINFORMATION].
messageBox
addCallback: XmNokCallback
receiver: self
selector: #ok:clientData:callData:
clientData: nil.
(messageBox getChild: XmDIALOGCANCELBUTTON) unmanageChild.
(messageBox getChild: XmDIALOGHELPBUTTON) unmanageChild.

shell realizeWidget
button: widget clientData: clientData callData: callData
"The button has been pressed. Pop up the message dialog."
messageBox manageChild
ok: widget clientData: clientData callData: callData
Transcript
cr; show: 'The OK button in the dialog has been pressed.';
cr; show: 'The dialog is about to disappear.'.

SelectionBox widgets
Selection-box widgets (CwSelectionBox) are used to obtain a selection from a list of
alternatives provided to the user. A selection box can contain some or all of the
following components: a scrollable list of alternatives, an editable text field for the
selected alternative, labels for the list and text field, and four buttons. By default,
the buttons are labelled OK, Apply, Cancel and Help.

A selection box is usually created in a dialog shell to prompt the user to select a
list item. Selection-box widgets are created using the createSelectionBox:argBlock: and
createSelectionDialog:argBlock: convenience methods. The latter method is similar to
the first, but also creates a CwDialogShell as the parent of the CwSelectionBox so that
it can be popped up separately from the main widget tree. As with pop-up menus
a selection dialog can be created and left unmanaged until it is to be presented to
the user. It is popped up by sending the selection box the manageChild message.

Chapter 7. Common Widgets 193


The default behavior is to remain open until either the OK or Cancel button is
pressed, or the dialogs close box is double-clicked. The application can explicitly
close the dialog by sending unmanageChild to the selection box.

The dialogType resource specifies which selection-box components actually appear.


It can have one of the following values:
XmDIALOGPROMPT
All standard children except the list and list label are created and all except
the Apply button are managed.
XmDIALOGSELECTION
All standard children are created and managed. This is the default if the
parent is a dialog shell.
XmDIALOGWORKAREA
All standard children are created, and all except the Apply button are
managed. This is the default if the parent is not a dialog shell.

When the selection box is in a dialog shell, the dialogTitle resource specifies the title
of the dialog shell, and the dialogStyle resource specifies the input mode while the
dialog is active.

The labels for the list widget and the text widget are set using the
selectionLabelString: and textLabelString: methods respectively.

An example selection dialog is shown in the diagram below.

CwSelectionBox Example
Select a bitmap file:
stars.bmp
stripes.bmp
cats.bmp
dogs.bmp
Desktop background:
cats.bmp

OK Apply Cancel Help

The list contents can be set or retrieved using the listItems resource. The text
contents can be set or retrieved using the textString resource.

The widgets comprising the selection box can be retrieved using the getChild:
method. If a button or other widget is not required by the application, it can be
retrieved and unmanaged. Possible arguments to the getChild: method are:
v XmDIALOGAPPLYBUTTON
v XmDIALOGCANCELBUTTON
v XmDIALOGDEFAULTBUTTON
v XmDIALOGHELPBUTTON
v XmDIALOGLIST
v XmDIALOGLISTLABEL
v XmDIALOGOKBUTTON
v XmDIALOGSELECTIONLABEL
v XmDIALOGSEPARATOR
v XmDIALOGTEXT

194 IBM Smalltalk: Programmers Reference


v XmDIALOGWORKAREA

The okCallback, applyCallback, cancelCallback, and helpCallback of the selection box are
run when the corresponding buttons are pressed. If a callback is not added, the
corresponding button will still appear, but nothing will happen when it is pressed.
When the selection box is in a dialog shell, it is unmanaged when the OK or
Cancel button is pressed, unless the autoUnmanage resource is set to false.

Using the mustMatch resource, the selection box can be configured to test whether
the text typed in the text widget matches any item in the list. If mustMatch is true
and the text does not match any item in the list when the OK button is pressed,
the noMatch callback is run, otherwise the ok callback is run. Note that if the
noMatch callback is run, a selection dialog will not be unmanaged.

The code below creates the following modeless selection dialog that is popped up
when a button is pressed. The name of the Apply button has been changed to
Show, and the Help button is hidden.

Selection Dialog
Items:
Item 1
Item 2
Item 3
Item 4
Item:
Item 2

OK Show Cancel

The mustMatch resource and noMatch callback are used to test when the text does
not match any item in the list. Because the dialog is modeless, the user can click or
type in any other widget without having to close the dialog first.
Object subclass: #SelectionDialogExample
instanceVariableNames: 'selectionBox '
classVariableNames: ''
poolDictionaries: 'CwConstants'

open
| items shell button |

items := OrderedCollection new.


1 to: 20 do: [:i |
items add: 'Item ', i printString].
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Selection Dialog Example'].
button := shell
createPushButton: 'Push me for selection box'
argBlock: nil.
button
addCallback: XmNactivateCallback
receiver: self
selector: #button:clientData:callData:
clientData: nil.
button manageChild.
selectionBox := shell
createSelectionDialog: 'selection'
argBlock: [:w |
w

Chapter 7. Common Widgets 195


dialogTitle: 'Selection Dialog';
dialogStyle: XmDIALOGMODELESS;
dialogType: XmDIALOGSELECTION;
listLabelString: 'Items:';
selectionLabelString: 'Selection:';
applyLabelString: 'Show';
listItems: items;
mustMatch: true].
selectionBox
addCallback: XmNokCallback
receiver: self
selector: #ok:clientData:callData:
clientData: nil;

addCallback: XmNapplyCallback
receiver: self
selector: #show:clientData:callData:
clientData: nil;
addCallback: XmNcancelCallback
receiver: self
selector: #cancel:clientData:callData:
clientData: nil;

addCallback: XmNnoMatchCallback
receiver: self
selector: #noMatch:clientData:callData:
clientData: nil.
(selectionBox getChild: XmDIALOGHELPBUTTON) unmanageChild.

shell realizeWidget
button: widget clientData: clientData callData: callData
"The button has been pressed. Pop up the selection dialog."

selectionBox manageChild
ok: widget clientData: clientData callData: callData
"The OK button in the dialog has been pressed.
The dialog will be automatically unmanaged."

Transcript cr; show: 'The final selection is: ' , widget textString
show: widget clientData: clientData callData: callData
"The Show button in the dialog has been pressed.
The dialog will NOT be automatically unmanaged."

Transcript cr; show: widget textString


cancel: widget clientData: clientData callData: callData
"The Cancel button in the dialog has been pressed.
The dialog will be automatically unmanaged."

Transcript cr; show: 'The selection dialog was canceled.'


noMatch: widget clientData: clientData callData: callData
"The OK button in the dialog was pressed,
but the textString did not match any item in
the items list. The dialog will NOT be automatically unmanaged."

Transcript cr; show: 'The selection does not match any item in the list.'

Dialog convenience methods


Common Widgets provides several dialog creation convenience methods. The
following methods create a CwMessageBox as the child of a CwDialogShell, and set
the dialogType resource as indicated so that an appropriate icon is displayed:
createMessageDialog:argBlock:
XmDIALOGMESSAGE

196 IBM Smalltalk: Programmers Reference


createErrorDialog:argBlock
XmDIALOGERROR
createInformationDialog:argBlock:
XmDIALOGINFORMATION
createQuestionDialog:argBlock:
XmDIALOGQUESTION
createWarningDialog:argBlock:
XmDIALOGWARNING
createWorkingDialog:argBlock:
XmDIALOGWORKING

The following methods create a CwSelectionBox as the child of a CwDialogShell, and


set the dialogType resource as indicated:
createSelectionDialog:argBlock:
XmDIALOGSELECTION
createPromptDialog:argBlock:
XmDIALOGPROMPT

Here are two additional dialog creation convenience methods:


createBulletinBoardDialog:argBlock:
Creates an empty CwBulletinBoard widget within a dialog shell, allowing
the application to define the entire contents of the dialog
createFormDialog:argBlock:
Creates an empty CwForm widget within a dialog shell, allowing the
application to define the entire contents of the dialog

Creating and using prompters


CommonWidgets provides three prompters that can be used to prompt the user for
information required by the application. The user must reply to the prompt before
the application can continue execution. The following three prompters are
provided:
CwMessagePrompter
Displays a message and waits for the user to press a button in response
CwTextPrompter
Displays a message and prompts the user for a single line of text as a reply
CwFileSelectionPrompter
Prompts the user for the path name of a file

Prompters are an extension of the OSF/Motif API implemented using the


platforms native prompters, where available. Prompters are not widgets, but
provide a convenient, portable mechanism to access platform-specific prompting
services.

The CwMessagePrompter provides functionality similar to the CwMessageBox widget


described earlier. The difference is that CwMessagePrompter uses the platforms
message prompter, whereas CwMessageBox provides the behavior of the Motif
message box. On Motif platforms, CwMessagePrompter is implemented using
CwMessageBox.

Chapter 7. Common Widgets 197


Prompters can be created in two ways:
v With an associated parent widget tree, using an expression such as
<prompterClass> for: aCwWidget
v Without an associated parent widget tree, using an expression such as
<prompterClass> new

A prompter can be configured with several settings, described below. Default


values are provided for all settings of a prompter.

The modality of a prompter can be changed using the prompterStyle: method. Valid
prompter styles, in order of restrictiveness, are:
XmPRIMARYAPPLICATIONMODAL
The user must respond to the prompter before being able to resume
interaction with the receivers parent window. Interaction with other
windows is still possible.
XmFULLAPPLICATIONMODAL
The user must respond to the prompter before being able to resume
interaction with any window belonging to the receivers application.
Interaction with windows from other applications is still possible.
XmSYSTEMMODAL
The user cannot interact with the windows of any application until
responding to the prompter.

If a platform does not support the specified prompter style, the style is promoted
to the next most restrictive style. If no more restrictive style exists, the style is
demoted to the next less restrictive style.

The title of the prompter can be set using the title: method.

A prompter is popped up using the prompt method. Control is not returned to the
application until the user responds to the prompter. The value returned by the
prompt method is nil if the user cancels the prompter. If the user replies to the
prompter without cancelling, the value returned depends on the type of prompter.

Message prompter
A CwMessagePrompter displays a message to the user and waits for a button to be
pressed. A message prompter from the OS/2 platform is illustrated below.

Message Prompter Example

Is this a message prompter?


?
Yes No Cancel

The message displayed by a message prompter is set using the messageString:


method.

The buttons displayed by the prompter can be changed using the buttonType:
method. Valid values are:
XmOK
OK button only

198 IBM Smalltalk: Programmers Reference


XmOKCANCEL
OK and Cancel buttons (default setting)
XmRETRYCANCEL
Retry and Cancel buttons
XmABORTRETRYIGNORE
Abort, Retry, and Ignore buttons
XmYESNO
Yes and No buttons
XmYESNOCANCEL
Yes, No, and Cancel buttons

The button selected by default if the user presses the return key can be changed
using the defaultButtonType:method. Valid values are:
XmDEFAULTBUTTON1
The first button is the default (default setting)
XmDEFAULTBUTTON2
The second button is the default
XmDEFAULTBUTTON3
The third button is the default

The icon displayed in the message box can be changed using the iconType: method.
Valid values are:
XmNOICON
No icon
XmICONINFO
Info icon (default setting)
XmICONWARNING
Warning icon
XmICONERROR
Error icon
XmICONQUESTION
Question icon

The prompt method returns one of the following values, depending on which
button was pressed:
true The OK, Yes, or Retry button was pressed
false The No or Abort button was pressed
nil The Cancel or Ignore button was pressed

The message prompter shown above is created by the following code.


| reply |
reply :=
CwMessagePrompter new
title: 'Message Prompter Example';
messageString: 'Is this a message prompter?';
buttonType: XmYESNOCANCEL;
defaultButtonType: XmDEFAULTBUTTON1;
iconType: XmICONQUESTION;
prompt.

Transcript cr; show: 'The reply was: ', reply printString

Chapter 7. Common Widgets 199


Text prompter
A CwTextPrompter prompts the user for a single line of text, in response to a query.
A text prompter from the OS/2 platform is illustrated below.

The message displayed by a text prompter is set using the messageString: method.

The initial contents of the text box for the users answer can be set using the
answerString: method.

The prompt method returns nil if the prompter was cancelled, or the contents of the
text box if the user pressed the return key or the OK button.

The text prompter shown above is created by the following code.


| reply |
reply :=
CwTextPrompter new
title: 'Text Prompter Example';
messageString: 'What is your name?';
answerString: 'Enter your name here';
prompt.

Transcript cr; show: 'The reply was: ', reply printString.

File selection prompter


A CwFileSelectionPrompter prompts the user to choose a file from a directory in the
file system. A file selection prompter from the OS/2 platform is shown in the
following illustration:

File Selection Prompter Example


Open filename:
*. *
Type of file: Drive:
<All Files> C: [C_DOS5]

File: Directory:
AUTOEXEC.BAT C:\
COMMAND.COM BIN
CONFIG.001 COMPLIB
CONFIG.SYS DOS5

OK Cancel

The following methods are used to configure a file selection prompter:

200 IBM Smalltalk: Programmers Reference


searchPath:
Sets the string specifying the initial directory path presented to the user.
The default is a platform-specific string denoting the current working
directory.
searchMask:
Sets the string used to filter the file names; an asterisk (*) within the string
denotes a wildcard. The default is a platform-specific string denoting all
normal files.
fileName:
Sets the string to use as the default file name presented to the user. The
default is an empty string.
accessMode:
Hint provided by the application to indicate whether the application
intends to open or save a file.
fileExtension:
Hint provided by the application to indicate the preferred file extension for
the return value. On some platforms, the prompter ensures that the return
value has the correct extension if fileExtension is set. The default is an
empty string.

The prompt method returns nil if the prompter is cancelled. If a file is properly
selected, a string specifying the full path name of the file is returned.

The file selection prompter shown above is created by the following code.
| reply |
reply :=
CwFileSelectionPrompter new
title: 'File Selection Prompter Example';
searchPath: 'c:\';
searchMask: '*.*';
prompt.

Transcript cr; show: 'The reply was: ', reply printString.

Extended widgets
Common Widgets provides a framework for developing custom widgets based on
existing widgets. These are called extended widgets. If the IBM Smalltalk portable
API is used to develop an extended widget, it will be portable between all
platforms supported by IBM Smalltalk. Extended widgets are often implemented
using a CwDrawingArea, with its visual appearance drawn using Common
Graphics calls, and with user input processed using event handlers.

Consider the following subset of the CwWidget class hierarchy:

CwWidget
CwBasicWidget
CwComposite
CwPrimitive
CwShell
CwExtendedWidget
CwExtendedComposite
CwExtendedPrimitive

Chapter 7. Common Widgets 201


The CwWidget class defines behavior common to all widgets. The CwBasicWidget
hierarchy provides the basic widgets described thus far, such as CwShell, CwText,
CwList, CwPushButton, CwForm and CwRowColumn. Basic widgets are implemented
using the native widgets provided by each platform. The implementation of basic
widgets is not portable.

The CwExtendedWidget class is the abstract superclass of all extended widgets. As


with the basic widget class hierarchy, it is divided up into primitive widgets
(CwExtendedPrimitive) and composite widgets (CwExtendedComposite).

Writing an extended widget


The first step in writing an extended widget is to create a subclass of the
appropriate extended widget framework class. Extended widgets that are not
intended to contain child widgets should be implemented as subclasses of
CwExtendedPrimitive. Those that are intended to contain child widgets should be
implemented as subclasses of CwExtendedComposite. It is important to understand
this difference:
v A subclass of CwExtendedPrimitive can be implemented using a primary widget
with child widgets, however an application programmer making use of this type of
extended widget can not add any children to it.
v A subclass of CwExtendedComposite can be implemented using just a single widget,
say for example a CwForm with no children, but the same application
programmer can create this type of extended widget and add as many children
as are permitted by the extended widgets API.

After the subclass has been created, it should define an instance variable for each
resource and callback provided by the extended widget, as well as instance
variables required for any other aspects of the widgets implementation.

Defining the extended widget class


An extended widget is implemented using a widget tree consisting of other basic
or extended widgets. This tree is called the primary widget tree. The root of the
primary widget tree is known as the primary widget. The extended widget class
must override the createPrimaryWidget:parent:argBlock: method. This method creates
and answers the primary widget, but does not create the children of the primary
widget. If the primary widget tree consists of more than one widget, the extended
widget class must override createWidgetSystem. This method creates the remainder
of the primary widget tree, that is, the children of self primaryWidget.

Initialization
Three methods can be overridden to initialize the state of the widget. The initialize
method is run as the first step in extended widget creation. It is useful for
initializing the internal state of the widget, except for resources. The
initializeResources method initializes the instance variables representing resources.
Both of these methods are run before the primary widget tree has been created.
The initializeAfterCreate method is run after the primary widget tree has been
created. It is useful for configuring widgets after they have been created, and for
initializing graphics resources.

Resources
Set and get accessor methods should be added for each resource provided by the
extended widget. Usually, the get method simply answers the corresponding
instance variable. The set method usually sets the corresponding instance variable
and makes any required changes in the primary widget tree.

202 IBM Smalltalk: Programmers Reference


Callbacks
Set and get accessor methods must be added for each callback provided by the
extended widget. The set accessor method simply sets the instance variable used to
store an ordered collection of CwCallbackRec objects for the particular callback. It is
typically only called by the get method to initialize the ordered collection. The
valueChangedCallback: method in the example that follows is a callback set accessor.

The get accessor method for a callback is a little more involved. In order to work
properly with methods inherited from CwExtendedWidget, the constant used to
specify the type of the callbackfor example, XmNactivateCallback for an activate
callbackis a pool variable that must be equal to the get methods selectorfor
example, activateCallback. The valueChangedCallback method in the example is a
callback get accessor. Callback type specifiers are already defined in the
CwConstants pool dictionary, however, if a new name is desired, it can be added to
an application-specific pool dictionary using the same naming convention.

The get accessor must answer an ordered collection, to which callback descriptors
are added whenever a callback is registered. If the callback resource is
uninitialized, the get method must set the callback resource to a new
OrderedCollection, and answer that.

Registered callbacks can be run by the extended widget using the


callCallbacks:callData: method. The example extended widget calls its valueChanged
callback in the valueChangedCallback: method.

Widget-specific methods
An extended widget works by forwarding most widget messages to its primary
widget. All of the methods inherited from CwWidget are automatically forwarded
to the primary widget if they are not explicitly overridden. In simple cases, an
extended widgets behavior can be implemented simply by adding resource and
callback methods as described above. For more complicated widgets, it is usually
necessary to extend the basic widget protocol by providing methods to support the
new operations on the extended widget.

Using an extended widget


After an extended widget class has been defined, application developers can create
instances of the extended widget by sending the createWidget:parent:argBlock: or
createManagedWidget:parent:argBlock: method to the extended widgets class. The
create argBlock should only set resources that are defined for the extended widget
or in CwWidget, and should not assume a particular underlying implementation.

Example: a primitive extended widget


Two example extended widgets are provided in the next two sections. The first,
below, is a subclass of CwExtendedPrimitive, and the second, on page Example: a
composite extended widget on page 207, is a subclass of CwExtendedComposite. The
prefix Cew is used to differentiate these new widgets from basic widgets. For
simplicity, the examples do not include robust error-checking, nor does each
widget provide a complete set of resources.

The CewEntryField widget, shown below, has a label on the left and a text box on
the right. It allows a user to enter text into its text box, and it invokes a
valueChanged callback if a new value is present when the user either hits the tab
key or clicks on a different widget.

The extended widget is implemented using a CwForm as the primary widget with
CwLabel and CwText children. A losingFocus callback on the CwText enables the

Chapter 7. Common Widgets 203


widget to test entered data, and possibly call any registered valueChanged callbacks.

Name : Your name here aCewEntryField

CwExtendedPrimitive subclass: #CewEntryField


instanceVariableNames: 'label value valueChangedCallback labelWidget textWidget '
classVariableNames: ''
poolDictionaries: ''

createPrimaryWidget: theName parent: parent argBlock: argBlock


"Private - Create and answer the basic widget that is the root of the
widget hierarchy for the receiver's widget system."

|self parent
createForm: theName, 'Form'
argBlock: argBlock
createWidgetSystem
"Private - Create the children of the receiver's primary widget which form the
widget hierarchy."
| pw |

pw := self primaryWidget.
self labelWidget:
(pw
createLabel: pw name , 'Label'
argBlock: [:w | w labelString: self label])
manageChild.
self textWidget:
(pw
createText: pw name , 'Text'
argBlock: [:w |
w
borderWidth: 1;
value: self value])
manageChild.
self textWidget
addCallback: XmNlosingFocusCallback
receiver: self
selector: #losingFocus:clientData:callData:
clientData: nil.
"Add form attachments for the two children."
self labelWidget
setValuesBlock: [:w |
w
topAttachment: XmATTACHFORM;
leftAttachment: XmATTACHFORM;
rightAttachment: XmATTACHNONE;
bottomAttachment: XmATTACHFORM].
self textWidget
setValuesBlock: [:w |
w
topAttachment: XmATTACHFORM;
leftAttachment: XmATTACHWIDGET;
leftWidget: self labelWidget;
rightAttachment: XmATTACHFORM;
bottomAttachment: XmATTACHFORM].
initializeResources
"Private - Set the default extended widget resource values. This is sent
during create with isCreated set to false. All extended resource
variables should be initialized to default values here."
label := String new.
value := String new.

204 IBM Smalltalk: Programmers Reference


initializeAfterCreate
"Private - Perform any widget-specific post-create initialization."
self primaryWidget
horizontalSpacing: 4;
verticalSpacing: 4;
borderWidth: 1.

"Resources that involve the children of the primary widget have to be set
after the children are created."
self labelWidget labelString: label.
self textWidget value: value.
label
"Answer the value of the label resource.
Resource type: String
Default setting: ''
Resource access: CSG
Description:
Specifies the string for the CewEntryField's label.
This label is to the left of the CewEntryField's text box."
|label
label: resourceValue
"Set the value of the label resource to resourceValue.
Resource type: String
Default setting: ''
Resource access: CSG
Description:
Specifies the string for the CewEntryField's label.
This label is to the left of the CewEntryField's text box."
label := resourceValue.
self isCreated
ifTrue: [labelWidget labelString: resourceValue]
value
"Answer the value of the value resource.
Resource type: String
Default setting: '
Resource access: CSG
Description:
Specifies the string for the CewEntryField's value.
This value is displayed in the CewEntryField's text box if
set using the #value: message, or if a valid string followed
by a tab key is entered by the user.
Note that while the user is typing, the string displayed in the
text box might not be the same as the CewEntryField's value."

|value
value: resourceValue
"Set the value of the value resource to resourceValue.
Resource type: String
Default setting: '
Resource access: CSG
Description:
Specifies the string for the CewEntryField's value.
This value is displayed in the CewEntryField's text box if
set using the #value: message, or if a valid string followed
by a tab key is entered by the user.
Note that while the user is typing, the string displayed in the
text box might not be the same as the CewEntryField's value."

value := resourceValue.
self isCreated
ifTrue: [textWidget value: resourceValue]
valueChangedCallback
"Private - Answer the value of valueChangedCallback."
valueChangedCallback isNil
ifTrue: [self valueChangedCallback: OrderedCollection new].
|valueChangedCallback

Chapter 7. Common Widgets 205


valueChangedCallback: resourceValue
"Set the value of the XmNvalueChangedCallback resource to resourceValue.
Resource type: OrderedCollection of CwCallbackRec
Default setting: OrderedCollection new
Resource access: C
Callback reason: XmCRVALUECHANGED
Calldata structure: CwValueCallbackData
Description:
Specifies the list of callbacks that is called when the value
of the CewEntryField has changed. The reason sent by the
callback is XmCRVALUECHANGED. The structure returned
by this callback is CwValueCallbackData."

valueChangedCallback := resourceValue.
labelWidget
"Private - Answer the value of labelWidget."
|labelWidget

labelWidget: aCwLabel
"Private - Set the value of labelWidget to aCwLabel."

labelWidget := aCwLabel.
textWidget
"Private - Answer the value of textWidget."
|textWidget

textWidget: aCwText
"Private - Set the value of textWidget to aCwText."

textWidget := aCwText.
losingFocus: widget clientData: clientData callData: callData
"Private - Process a losing focus callback for the primary widget."
| newValue |

newValue := self textWidget value.

"If the new value is different, invoke the entryField widget's


valueChanged callback."
self value = newValue
ifTrue: [
self
value: newValue;
callCallbacks: XmNvalueChangedCallback
callData: (CwValueCallbackData new value: newValue)].

Using the CewEntryField primitive extended widget


The following code creates a CewEntryField instance, sets its name and label
resources inside the create argBlock, and hooks a valueChanged callback to it. The
new widget is shown in the diagram at the beginning of this section, on page
Example: a primitive extended widget on page 203.
| shell entryField |

shell := CwTopLevelShell
createApplicationShell: 'CewEntryField Test'
argBlock: nil.
entryField := CewEntryField
createManagedWidget: 'entryField'
parent: shell
argBlock: [:w |
w
label: 'Name :';
value: 'Your name here'].

206 IBM Smalltalk: Programmers Reference


entryField
addCallback: XmNvalueChangedCallback
receiver: self
selector: #valueChanged:clientData:callData:
clientData: nil.
shell realizeWidget.

valueChanged: widget clientData: clientData callData: callData


"Display the new value on the transcript."

Transcript cr; show: 'Value changed to: ' , callData value printString.

The CewEntryField class can be subclassed to provide a slightly different extended


widget by simply overriding one method, as in the following class definition for
CewNumericEntryField:
CewEntryField subclass: #CewNumericEntryField
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''

losingFocus: widget clientData: clientData callData: callData


"Private - Process a losing focus callback for the primary widget."

| newValue |

newValue := self textWidget value.


"Verify that the new value string represents a number.
If it doesn't, reset the text widget and return."
(newValue notEmpty and: [newValue conform: [:c | c isDigit]])
ifFalse: [ |self value: value ].
"If the new value is different, invoke the entryField widget's
valueChanged callback."

self value = newValue


ifTrue: [
self
value: newValue;
callCallbacks: XmNvalueChangedCallback
callData: (CwValueCallbackData new value: newValue)]

Example: a composite extended widget


The CewTitleFrame widget draws a rounded-corner rectangle around its single
child, and displays its title in the upper-left portion of the rectangle. This widget is
similar to CwFrame, and, like CwFrame, does not support any special callbacks. Its
purpose is to provide decoration, not interactive behavior.

The extended widget is implemented using a CwDrawingArea for the primary


widget, with an expose callback to draw the frame and title, and a resize callback
to make sure the child always fits inside the frame.

Direction
Up aCewTitleFrame
Down with a 2-button radio box child

CwExtendedComposite subclass: #CewTitleFrame


instanceVariableNames: 'title borderInset childInset radius angles gc
lineSegments arcOrigins'
classVariableNames: ''
poolDictionaries: ''

Chapter 7. Common Widgets 207


createPrimaryWidget: theName parent: parent argBlock: argBlock
"Private - Create and answer the basic widget that is the root of
the widget hierarchy for the receiver's widget system."

|parent
createDrawingArea: theName , 'DrawingArea'
argBlock: argBlock
initialize
"Private - Perform any private widget-specific state initialization. This is sent
before any other initialization begins. borderInset, radius, and angles are needed
for drawing the frame. lineSegments is set to nil to ensure that the lineSegments
and arcOrigins collections are calculated on first expose. childInset used to size
child in resize callback."
self
radius: 15;
angles: #(90 0 270 180);
borderInset: (CgFontStruct default height) // 2 + 4;
childInset: self borderInset * 2.
initializeResources
"Private - Set the default extended widget resource values. This is sent during
create with isCreated set to false. All extended resource variables should be
initialized to default values here."
title := String new.
initializeAfterCreate
"Private - Perform any widget-specific post-create initialization."
self primaryWidget
marginHeight: self childInset;
marginWidth: self childInset;
addCallback: XmNexposeCallback
receiver: self
selector: #expose:clientData:callData:
clientData: nil;
addCallback: XmNresizeCallback
receiver: self
selector: #resize:clientData:callData:
clientData: nil.
title
"Answer the value of the title resource.
Resource type: String
Default setting: ''
Resource access: CSG
Description:
Specifies the string for the CewTitleFrame's title.
This title is displayed in the upper left portion of the
rounded-corner rectangle that frames the child widget."
|title
title: resourceValue
"Set the value of the title resource to resourceValue.
Resource type: String
Default setting: ''
Resource access: CSG
Description:
Specifies the string for the CewTitleFrame's title.
This title is displayed in the upper left portion of the
rounded-corner rectangle that frames the child widget."
title := resourceValue.
borderInset
"Private - Answer the value of borderInset."
|borderInset

208 IBM Smalltalk: Programmers Reference


borderInset: anInteger
"Private - Set the value of borderInset to anInteger."

borderInset := anInteger.
childInset
"Private - Answer the value of childInset."
|childInset

childInset: anInteger
"Private - Set the value of childInset to anInteger."

childInset := anInteger.
radius
"Private - Answer the value of radius."
|radius

radius: anInteger
"Private - Set the value of radius to anInteger."

radius := anInteger.
angles
"Private - Answer the value of angles."
|angles

angles: anArray
"Private - Set the value of angles to anArray."

angles := anArray.
lineSegments
"Private - Answer the value of lineSegments."
|lineSegments

lineSegments: anOrderedCollection
"Private - Set the value of lineSegments to anOrderedCollection."

lineSegments := anOrderedCollection.
arcOrigins
"Private - Answer the value of arcOrigins."
|arcOrigins

arcOrigins: anArray
"Private - Set the value of arcOrigins to anArray."

arcOrigins := anArray.
gc
"Private - Answer the value of gc. Create if not already created."
gc isNil
ifTrue: [self initializeGraphics].
|gc

gc: aCgGC
"Private - Set the value of gc to aCgGC."

gc := aCgGC
initializeGraphics
"Private - Set the receiver's palette and create a GC. This method is called
by the #gc method if gc is nil."
| pw colors |
pw := self primaryWidget.
colors := Array
with: pw backgroundColor "pixel 0"
with: pw foregroundColor. "pixel 1"

"The palette must be set on the shell window."

Chapter 7. Common Widgets 209


pw shell window
setPalette: (CgIndexedPalette colors: colors).
self gc: (pw window
createGC: GCForeground | GCBackground | GCFont
values: (CgGCValues new
background: 0;
foreground: 1;
font: pw display defaultFont)).
recalculateSegmentsAndArcs
"Private - Calculate the line segments and arc origins to draw the frame around
the child widget at the current size."
| border offset diam width height |

border := self borderInset.


offset := border + self radius.
diam := self radius * 2.
width := self width.
height := self height.
self lineSegments: (OrderedCollection new
add: (CgSegment
point1: offset @ border
point2: (width - offset) @ border);
add: (CgSegment
point1: (width - border) @ offset
point2: (width - border) @ (height - offset));
add: (CgSegment
point1: (width - offset) @ (height - border)
point2: offset @ (height - border));
add: (CgSegment
point1: border @ (height - offset)
point2: border @ offset)).
self arcOrigins: (Array
with: (border @ border)
with: (width - (diam + border)) @ border
with: (width @ height) - (diam + border)
with: border @ (height - (diam + border))).
expose: widget clientData: clientData callData: callData
"Private - Process an expose callback for the primary widget by
drawing the rounded-corner rectangle frame and title."
| border offset diam |
border := self borderInset.
offset := border + self radius.
diam := self radius * 2.
self lineSegments isNil
ifTrue: [self recalculateSegmentsAndArcs].

widget window
drawSegments: self gc segments: self lineSegments.

self arcOrigins
with: self angles do: [ :p :angle |
widget window
drawArc: self gc
x: p x
y: p y
width: diam
height: diam
angle1: 64 * angle
angle2: 64 * 90 ].
widget window
drawImageString: self gc
x: offset + 20
y: border + (widget display defaultFontStruct ascent // 2)
string: ' ' , self title , ' '.

210 IBM Smalltalk: Programmers Reference


resize: widget clientData: clientData callData: callData
"Private - Process a resize callback for the primary widget by
resizing the primary widget's child to fit inside the frame."
| child offset |

(child := widget children) notEmpty


ifTrue: [
offset := self childInset * 2.
child first
resizeWidget: self width - offset
height: self height - offset
borderWidth: child first borderWidth ].
"Force a recalculation of line segments and arc origin based on the new size.
Recalculation will occur at next expose."
self lineSegments: nil.

"Clear the widget and force an expose event."


widget window clearArea: 0 y: 0 width: 0 height: 0 exposures: true.

Using the CewTitleFrame composite extended widget


The following code creates a CewTitleFrame instance with a radio-box child (a
CwRowColumn with radioBehaviour set to true). The radio box then creates two
CwToggleButton children. This is shown in the diagram at the beginning of this
section, on page Example: a composite extended widget on page 207.
| shell titleFrame radioBox |
shell := CwTopLevelShell
createApplicationShell: 'CewTitleFrame Test'
argBlock: nil.

titleFrame := CewTitleFrame
createManagedWidget: 'titleFrame'
parent: shell
argBlock: [:w | w title: 'Direction'].
(radioBox := titleFrame
createRadioBox: 'radio'
argBlock: nil)
manageChild.

(radioBox
createToggleButton: 'Up'
argBlock: [:w | w set: true])
manageChild.
(radioBox
createToggleButton: 'Down'
argBlock: nil)
manageChild.

shell realizeWidget

Fonts
The font used by certain widgets can be specified by the application. The following
widgets allow their font to be changed: CwLabel, CwPushButton, CwToggleButton,
CwCascadeButton, CwText, CwList, CwComboBox, and CwScale. The font is changed
using the fontList: method. The font to use is specified by a CwFontList object.

To create a CwFontList, the fontStruct: class method of CwFontList is passed a


CgFontStruct describing a Common Graphics font. A CgFontStruct can be loaded
using the loadQueryFont: method of CgDisplay. For further details on fonts, consult
Using fonts on page 93.

Chapter 7. Common Widgets 211


The following code creates a multiline text widget and sets its font to the
monospaced font named 8x13.
| shell fontStruct fontList text |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Font List Example'].
fontStruct := shell display loadQueryFont: '8x13'.
fontList := CwFontList fontStruct: fontStruct.

text := shell
createText: 'text'
argBlock: [:w | w
editMode: XmMULTILINEEDIT;
fontList: fontList].
text setString: 'This text is displayed using the 8x13 font.'.
text manageChild.

shell realizeWidget

Using the system browser font


All of the browsers in IBM Smalltalk are subclasses of EtWindow. This class keeps
one font that every browser uses. You can find the browser font name by
evaluating:
EtWindow fontName.

You can change the browser font from the File menu. If the browser font has not
been changed, then the EtWindow class method fontName returns nil. If your
window will use the browser font, then you can make the window a subclass of
EtWindow. Your subclass should provide the instance method fontSettableWidgets,
which answers a collection of all the widgets to be notified in case the font
changes. EtWindow calls all of these widgets for you and tells them to change to
the new font.

You can still use the browser font, even if your window does not subclass
EtWindow. The following example creates a new window with the system font. The
class method fontList in EtWindow returns either the current CwFontList, or nil if the
font has not been changed.
|shell text fontList|
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Browser Font' ].

fontList := EtWindow fontList.


fontList isNil ifTrue: [
fontList := CwFontList fontStruct:
(CgDisplay default defaultFontStruct) ].
text := shell
createText: 'text'
argBlock: [:w | w
columns: 60;
editMode: XmMULTILINEEDIT;
fontList: fontList ].

text setString: 'This font is the system browser font.'.


text manageChild.
shell realizeWidget

212 IBM Smalltalk: Programmers Reference


Colors
The background and foreground color of widgets can be set and queried by the
application using the backgroundColor and foregroundColor resources. The
foregroundColor is used for text or other foreground graphics, and the
backgroundColor is used to fill the background of the widget. The color values are
specified using CgRGBColor objects which allow the application to specify the red,
green, and blue components of the desired color. See Specifying colors on
page 113 for more information concerning the use of CgRGBColor objects. There are
platform-specific limitations concerning setting the colors of certain widgets. See
Appendix E. Common widgets platform differences on page 517 for the details of
these limitations.

Tip: Due to platform-specific limitations, a widget might not take on a requested


color setting, or it might take on a slightly different color setting than
requested. To determine the exact color a widget is using, the resource can be
queried after it is set. Querying the color resource always returns the color the
widget is actually using. For details on platform limitations, see Appendix.

The following code creates a multiline text widget and sets its foregroundColor to
black and its backgroundColor to blue.
| shell text |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Color Example'].
text := shell