You are on page 1of 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
createText: 'text'
argBlock: [:w | w
editMode: XmMULTILINEEDIT;
columns: 50;
rows: 4;
value: 'Hello';
foregroundColor: CgRGBColor white;
backgroundColor: (shell screen lookupColor: 'blue')].
text manageChild.

shell realizeWidget

Clipboard operations
Common Widgets provides operations to interact with the platform clipboard,
which holds data to be transferred between applications. An application can copy
data from Smalltalk to the clipboard, and paste data from the clipboard into
Smalltalk.

Data stored in the clipboard has an associated format name, which applications use
to identify the type of data. Common Widgets automatically handles two
predefined formats, string and pixmap, which are mapped to the corresponding
platform-specific format during clipboard operations. Applications can also work
with platform-specific formats, or define new proprietary formats. The format
names and corresponding buffer object classes used to represent data when
working with the clipboard include the following:
STRING
String
PIXMAP
CgPixmap
All others
ByteArray

Chapter 7. Common Widgets 213


The class CgDisplay implements the clipboard operations. All operations require a
CgWindow parameter, which identifies the application to the OS window system.
The window can be any valid CgWindow. The available clipboard operations
include the following:
clipboardStartCopy:clipLabel:itemIdReturn:
Sets up OS clipboard storage and data structures to receive data
clipboardCopy:itemId:formatName:buffer:privateId:
Copies data to the clipboard
clipboardCancelCopy:itemId:
Cancels a copy operation that has been started
clipboardEndCopy:itemId:
Ends a copy to the clipboard
clipboardInquireCount:countReturn:
Returns the number of data item formats in the clipboard
clipboardInquireFormat:index:formatNameReturn:
Returns the format of a data item
clipboardInquireLength:formatName:lengthReturn:
Returns the length of the stored data
clipboardStartRetrieve:
Starts a copy from the clipboard
clipboardRetrieve:formatName:bufferReturn:privateIdReturn:
Retrieves a data item from the clipboard
clipboardEndRetrieve:
Ends a copy from the clipboard
clipboardLock:
Locks the clipboard
clipboardUnlock:
Unlocks the clipboard
clipboardRegisterFormat:formatLength:
Registers a new format

Examples for using the clipboard


The following examples show how to copy String data to and from the clipboard.

Example 1: copying data to the clipboard


| window itemId copyText |

window := CgWindow default.


copyText := 'This text will be copied'.
itemId := ReturnParameter new.

"Set up storage and data structures."


window display
clipboardStartCopy: window
clipLabel: 'STRING'
itemIdReturn: itemId.
"Copy a data item of String format to temporary storage."
window display
clipboardCopy: window
itemId: itemId value
formatName: 'STRING'

214 IBM Smalltalk: Programmers Reference


buffer: copyText
privateId: 0.

"End the clipboard transaction."


window display
clipboardEndCopy: window
itemId: itemId value.

Example 2: retrieving data from the clipboard


| window status bufferHolder lengthHolder |

window := CgWindow default.

"Ask for the length of the string in the clipboard."


status := window display
clipboardInquireLength: window
formatName: 'STRING'
lengthReturn: (lengthHolder := ReturnParameter new).
status = ClipboardSuccess
ifTrue: [
Transcript cr; show: 'XmClipboardStatus...', status printString; cr.
|self].

"Retrieve a string from the clipboard and place it in bufferHolder."


status := window display
clipboardRetrieve: window
formatName: 'STRING'
bufferReturn: (bufferHolder := ReturnParameter new)
privateIdReturn: ReturnParameter null. "Ignore result"
status = ClipboardSuccess
ifTrue: [
Transcript cr; show: 'XmClipboardStatus...', status printString; cr.
|self].
|bufferHolder value

Platform-integrated drag and drop


Common Widgets provides platform-integrated drag and drop capabilities to
VisualAge applications. Common Widgets drag and drop gives applications access
to the native drag and drop systems of each platform, and minimizes the need to
write platform-specific code. Common Widgets drag and drop is based on the
OSF/Motif R1.2 drag and drop model.

Platform-integrated drag and drop involves two sides: the source and the
destination. The source and destination can be the same application or different
applications. For example, a user can drag from a widget that is part of application
A and drop on that same widget, on another widget in application A, or on a
widget in application B.

When a user performs a valid drop, data is transferred from the source to the
destination. In Common Widgets drag and drop, data transfer can be done at
several levels of complexity. The levels of data transfer include the following,
beginning with the simplest:
v Data transfer using built-in types. For example, String data can be transferred
using STRING and CgPixmap data can be transferred using PIXMAP.
v ByteArray data transfer. This is the default.
v Custom data transfer, which enables you to add to the set of built-in types.

To complete the first two levels of data transfer, you do not need to write
platform-specific code. These types of data transfer are described in this section. To

Chapter 7. Common Widgets 215


complete a custom data transfer you must extend Common Widgets drag and drop
by writing custom converters, which is not covered in this manual.

Target types
A target type is a string that represents the type of data transfer to perform when
a valid drop occurs. Target types are equivalent to format names, which are used
by the Common Widgets clipboard. Common Widgets drag and drop provides two
built-in target types that are supported on all platforms:
STRING
Specifies that a String is transferred
PIXMAP
Specifies that a CgPixmap is transferred

Also can also use their own target types, for example, EMPLOYEE. When
Common Widgets drag and drop encounters a target type that is not built in, a
ByteArray is transferred. An application must provide a ByteArray on the source
side, and interpret the ByteArray data on the destination side. ByteArray data
transfer is a flexible way to transfer data without writing any platform-specific
code.

Transferring data
Three types of data transfers can be performed when a valid drop occurs: move
(XmDROPMOVE), copy (XmDROPCOPY), or link (XmDROPLINK). The type of
transfer chosen depends on the advertised capabilities of each side involved in the
transfer and on the state of the keyboard at the time of the drop. If no type of
transfer can be chosen (XmDROPNOOP), then the drop is invalid. The user can
affect the type of transfer chosen by pressing the following keys while dragging:
Shift Forces a move if both sides support moving, otherwise no type of transfer
can be chosen
Ctrl Forces a copy if both sides support copying, otherwise not type of transfer
can be chosen
Ctrl+Shift
Forces a link is both sides support linking, otherwise no type of transfer
can be chosen

If no key is pressed while dropping, then the system chooses the first of move,
copy, or link (in that order) that is supported by both sides. If no type of transfer is
supported by both sides, then no type of transfer can be chosen.

Applications must ensure that the semantics of the data transfer are consistent with
the following conventions:
Move The destination first gets a copy of the data, then tells the source to delete
the data.
Copy The source and destination both keep a copy of the data.
Link The source keeps the only copy of the data, and the destination establishes
a link to that copy.

216 IBM Smalltalk: Programmers Reference


Drag and drop objects
A drag and drop object is like a widget. It has resources that can be set and
retrieved, and a mechanism for notifying an application of user actions. Most drag
and drop object resources are set in the create argBlock. The notification
mechanism is implemented using procs.

Procs
A proc is like a callback. It can be hooked to a drag and drop object to notify an
application of various events in the drag and drop process. A proc is specific to the
type of object and is represented by an instance of CwCallbackRec. When called, the
third parameter sent to a proc is a callData object created by Common Widgets
drag and drop.

Unlike callbacks, only one of each type of proc can be hooked to a drag and drop
object at any given time, and most procs are mandatory. A proc must be hooked in
the create argBlock using a proc resource setter. The first parameter sent to a proc
is a drag and drop object or a widget, depending on the proc.

Common Widgets drag and drop classes


The public API of Common Widgets drag and drop includes the following classes,
along with some methods in CwWidget and in callback data classes.

CwDragDropObject hierarchy

The CwDragDropObject class implements drag and drop objects. CwDragDropObject


is an abstract class that provides resource and proc methods to its subclasses. The
hierarchy looks like the following:

CwDragDropObject
CwDragContext
CwDropSite
CwDropTransfer

CwDropTransferEntry

The CwDropTransferEntry class is used in the data transfer process. Instances of


CwDropTransferEntry contain a targetType and a transferClientData object.

Drag source widgets and the CwDragContext object


A drag source widget has a dragDetectCallback hooked that sends the
dragStart:argBlock: method of CwWidget. To start a drag from a drag source widget,
the user holds down a mouse button while over the widget and moves the mouse
to a different location.

Note: The mouse button that initiates a drag is platform dependent. On Windows
platforms, the left mouse button triggers a dragDetectCallback. On OS/2, the
right mouse button triggers it. And, on OSF/Motif platforms, the middle
mouse button triggers it.

Some platforms allow dragging with a button other than those listed above.
To implement this behavior, applications call dragStart:argBlock: from a
button event handler.

The following example hooks a dragDetectCallback to a specified widget:

Chapter 7. Common Widgets 217


hookDragDetect: aCwWidget
"Hook a dragDetect callback to the specified widget."
aCwWidget
addCallback: XmNdragDetectCallback
receiver:self
selector: #dragDetect:clientData:callData:
clientData: nil.

When a drag begins over the widget, dragDetect:clientData:callData: is sent to the


receiver. The parameters to this callback are as follows:
widget The CwWidget to which the dragDetact callback hooks (aCwWidget)
clientData
The clientData specified when the dragDetect callback was hooked (nil)
callData
An instance of CwDragDetectCallbackData.

The CwDragDetectCallbackData instance contains a ButtonPress event that can


determine the mouse position and button when the button is pressed. The callData
reason is either XmCRDRAG or XmCRDRAGSELECT.

On platforms that allow the left mouse button to initiate a drag, normal drag
selection might cause the dragDetectCallback to be amibiguous in certain widgets,
such as text and list widgets. In these cases, the callData reason will be
XmCRDRAGSELECT and the application can decide whether to allow the drag
selection to continue, or set the callData doit to false and start a drag.

OSF/Motif platforms provide default drag behavior in text, label, and button
widgets. Common Widgets drag and drop turns off this behavior in a widget when
a dragDetect callback is added.

Starting the dragCwDragContext


An application starts a drag from a dragDetect callback handler by sending
dragStart:argBlock: with a CwDragContext create argBlock to the widget that received
the callback. Sending dragStart:argBlock: creates a CwDragContext which represents
the drag source for the duration of the drag and drop operation. Only one instance
of CwDragContext can exist at any time, and the instance ceases to exist when the
drag and drop operation is complete.

Note: The dragStart:argBlock: message must only be sent from a dragDetect callback
handler or a button press event handler. The first parameter must be the
button press event that initiated the drag.

The following drag context resources can be set in the create argBlock when the
drag starts:
sourceCursorAll
The CgIcon that replaces the mouse cursor during dragging.
dragOperations
The operations that the drag source supports.
exportOperations
The target types that the drag source supports, in order of source
preference with the most complete and accurate representation of the data
first.
convertProc
A proc that is received one or more times after a valid drop, in which the

218 IBM Smalltalk: Programmers Reference


source application must provide the dragged data in the format specified
by the target type in the procs callData. The convert proc will be called as
many time as the destination requests, each time with a different target
type. A source must be preparted to provide the data in the proper format
for any of the export targets it supports. It passes the data to the
destination by filling in the value field of the the procs callData.

The following code shows a typical dragDetect callback handler. It starts a drag,
specifying the operations and targets that the drag source supports and hooking a
convertProc to handle the source side of the data transfer. Assume that self dragIcon
answers a CgIcon.
dragDetectCallback: aCwWidget clientData: clientData callData: callData
"Handle the dragDetect callback for the receiver."
| startDrag |
(startDrag := callData reason = XmCRDRAG)
ifFalse: [
"The reason is XmCRDRAGSELECT.
Decide if the user is doing a drag or a select."
(self checkDragSourceCoordinates: callData event x @ callData event y)
ifTrue: [
"Interpret the mouse action as a drag operation
rather than a selection operation. Set the doit flag to false to
prevent the widget from doing normal selection processing."
callData doit: false.
startDrag := true]].
startDrag
ifTrue: [
aCwWidget
dragStart: callData event
argBlock: [:dragContext |
dragContext
sourceCursorIcon: self dragIcon;
dragOperations: XmDROPMOVE | XmDROPCOPY;
exportTargets: #('STRING');
convertProc: (CwCallbackRec
receiver: self
selector: #convertProc:clientData:callData:
clientData: aCwWidget)]].

Drop site widgets and CwDropSite objects


A drop site widget is a widget that has been registered as a drop site. The user can
drop onto a drop site widget by first dragging from a drag source widget, and
then moving the mouse over to the drop site widget and releasing the mouse
button.

Preparing to accept drops: CwDropSite


The application registers a widget as a drop site by sending it the dropSiteRegister:
message of CwWidget with a CwDropSite create argBlock. Sending dropSiteRegister:
creates a CwDropSite which represents the drop site during any drag and drop
operation. The following drop site resources can be set in the create argBlock:
dropSiteOperations
The operations that the drop site supports
importTargets
The target types that the drop site supports, in order of drop site
preference

Chapter 7. Common Widgets 219


dragProc
An optional proc that is received whenever the mouse moves over the
drop site widget during a drag. The callData reason can be one of the
following:
Enter XmCRDROPSITEENTERMESSAGE
Motion
XmCRDRAGMOTIONMESSAGE
Leave XmCRDROPSITELEAVEMESSAGE
Operation changed
XmCROPERATIONCHANGEDMESSAGE

The destination application can check the location of the mouse to


determine whether a drop in that location is acceptable, and modify the
operation and dropSiteStatus fields of the procs callData accordingly.
Modifications to these fields produces a corresponding change in the
appearance of the drag cursor. In addition, a drag proc can be used to
provide visual feedback to the user about the drop site. For example, to
indicate that the cursor is over some semantic area in the widget, an
application might draw a border around the area.
dropProc
A proc that is received when a mouse button is released over the drop site.
The destination application must determine whether the drop operation is
acceptable in that location. If the operation is valid, it must decide which
target types to request and then ask the drag context to start the data
transfer using those types.

The following code registers the specified widget as a drop site. This establishes
the operations and targets that the drop site supports, as well as hooking a
dragProc and a dropProc:
registerDropSite: aCwWidget
"Register the specified widget as a drop site."
aCwWidget
dropSiteRegister: [:dropSite |
dropSite
dropSiteOperations: XmDROPMOVE | XmDROPCOPY;
importTargets: #('STRING');
dragProc: (CwCallbackRec
receiver: self
selector: #dragProc:clientData:callData:
clientData: nil);
dropProc: (CwCallbackRec
receiver: self
selector: #dropProc:clientData:callData:
clientData: nil)].

Note: OSF/Motif platforms provide default drop behavior in text widgets.


Common Widgets drag and drop turns off this behavior in a widget when
the widget is registered as a drop site.

The drag proc


The dragProc resource is optional. A destination application can supply a drag
proc when it creates a drop site if it wants to perform special drag-under
animation, or select the operation based on some internal state or the semantic
location of the cursor in the drop site. The parameters to the dragProc are as
follows:
widget The CwWidget that was registered as a drop site
clientData
The clientData specified when the dragProc was hooked

220 IBM Smalltalk: Programmers Reference


callData
An instance of CwDragProcCallbackData

The following code shows a dragProc handler which does drag-under animation
based on the coordinates of the mouse in the callData, and chooses the operation
based on the mouse coordinates and the valid operations in the callData. Assume
the following:
v dropRectanglesFor: answers a collection of rectangles that are acceptable drop
areas in the given widget.
v operationForRectangle:widget:callData: answers the operation chosen by the
destination application, given a drop rectangle in the widget, and the operation,
operations and dropSiteStatus of the callData.
v animateRectangle:widget:operation: does drag-under animation for a drop rectangle
in a given widget, based on the selected operation.
v removeAllAnimation: removes all drag-under animation effects from the given
widget.
dragProc: widget clientData: clientData callData: callData
"Handle the drag proc for the receiver."
| point rectangle op |

"Turn off platform drag-under animation."


callData animate: false.
point := callData x @ callData y.
(self dropRectanglesFor: widget)
do: [:rect |
(rect containsPoint: point)
ifTrue: [rectangle := rect]].
rectangle isNil
ifTrue: [op := XmDROPNOOP]
ifFalse: [op := self
operationForRectangle: rectangle
widget: widget
callData: callData].
op = XmDROPNOOP
ifTrue: [
"The point is not in a drop rectangle for the widget,
or the operation is not valid for the drop rectangle."
callData
operation: XmDROPNOOP;
dropSiteStatus: XmDROPSITEINVALID.
self removeAllAnimation: widget]
ifFalse: [
"The point is in one of the drop rectangles for the widget,
and the operation is valid."
callData
operation: op;
dropSiteStatus: XmDROPSITEVALID.
self animateRectangle: rectangle widget: widget operation: op].
callData reason = XmCRDROPSITELEAVEMESSAGE
ifTrue: [self removeAllAnimation: widget].

The drop proc


The destination application must supply a drop proc when it creates a drop site. In
the drop proc, the destination determines if the drop is valid, and starts the drop
data transfer. The parameters to the dropProc are as follows:
widget The CwWidget that was registered as a drop site
clientData
The clientData specified when the dropProc was hooked

Chapter 7. Common Widgets 221


callData
An instance of CwDropProcCallbackData

Note: Some platforms allow the user to press the help key (usually F1) during a
drag. In this case, a drop proc is sent with the dropAction field in the callData
set to XmDROPHELP.

Two different dropProc handlers are shown in the following code. Both examples
call startTransfer:targetTypes:clientData:. The code for this method is shown in the
following section on the CwDropTransfer object.

The first example shows a very simple dropProc handler. This drop site only
supports the STRING target type. If the dropProc is called, the drop site knows
that the operation is valid and the drag source also supports STRING, therefore it
can simply ask for the data in STRING format.
dropProc: widget clientData: clientData callData: callData
"Handle the drop proc for the receiver."
callData dropAction = XmDROPHELP
ifTrue: [
"Help is not supported, therefore this is an invalid operation."
|callData dropSiteStatus: XmDROPSITEINVALID].
"A valid drop has occurred. Start a transfer, requesting our only target type."
self startTransfer: callData targetTypes: #('STRING') clientData: widget.

The second example dropProc is more complex. This drop site supports more than
one target type. In addition, the drop site cares about the semantic location of the
mouse, as did the example dragProc in the previous subsection, and it may decide
that the operation is invalid. Assume that intersectionOf: answers an
OrderedCollection containing the intersection of two collections, and that
dropRectanglesFor: and operationForRectangle:widget:callData: are the same methods
used in the dragProc example.
dropProc: widget clientData: clientData callData: callData
"Handle the drop proc for the receiver."
| point rectangle op exportTargets importTargets intersection |

callData dropAction = XmDROPHELP


ifTrue: [
"Help is not supported, therefore this is an invalid operation."
|callData dropSiteStatus: XmDROPSITEINVALID].
point := callData x @ callData y.
(self dropRectanglesFor: widget)
do: [:rect |
(rect containsPoint: point)
ifTrue: [rectangle := rect]].
op := rectangle isNil
ifTrue: [XmDROPNOOP]
ifFalse: [self operationForRectangle: rectangle
widget: widget callData: callData].
op = XmDROPNOOP
ifTrue: [
"The point is not in a drop rectangle for the widget,
or the operation is not valid for the drop rectangle."
callData
operation: XmDROPNOOP;
dropSiteStatus: XmDROPSITEINVALID]
ifFalse: [
"The point is in one of the drop rectangles for the widget,
and the operation is valid."
callData operation: op].

222 IBM Smalltalk: Programmers Reference


callData dropSiteStatus = XmDROPSITEVALID
ifTrue: [
"Valid drop. Start a transfer, requesting all possible target types."
exportTargets := callData dragContext exportTargets.
importTargets := widget dropSite importTargets.
intersection := self intersectionOf: exportTargets and: importTargets.
self startTransfer: callData targetTypes: intersection clientData: widget].

Data transfer and the CwDropTransfer object


The data transfer is the final stage in the drag and drop process. Both the source
application and the destination application are involved in the data transferthe
source through the convertProc (described previously), and the destination through
the transferProc (described below). The destination starts the data transfer from a
dropProc by specifying a list of one or more target types it would like the source to
provide. Common Widgets drag and drop takes over at this point. For each target
type requested, it sends a convertProc to the source to get the data, and then sends
a transferProc to the destination, containing the data from the source.

A destination can add target types to the list of requests during the transfer
process if necessary, permitting it to change what it requests based on data
received. If the operation was a move, the destination must add the special
DELETE target to the request list, which the system will send to the convertProc,
so that the source knows when it is safe to delete the data.

Starting the data transferCwDropTransfer


The data transfer is started from a dropProc handler by sending the
dropTransferStart: message, with a CwDropTransfer create argBlock, to the drag
context, which is provided in the dropProcs callData.

Sending dropTransferStart: creates a CwDropTransfer which represents the data


transfer in progress. There can only be one instance of CwDropTransfer in existence
at any time and it ceases to exist when the data transfer is complete. The following
drop transfer resources can be set in the create argBlock when the transfer is
started:
dropTransfers
An OrderedCollection of CwDropTransferEntry objects. There is one entry for
each target type requested by the drop site.
transferStatus
The status of the transfer. It can be either success
(XmTRANSFERSUCCESS), which is the default, or failure
(XmTRANSFERFAILURE). transferStatus can be updated by the destination
application.
transferProc
A proc that is received one or more times after a valid drop, in which the
destination application must use the data converted by the source
application. The target type and converted value of the data are given in
the procs callData. The transfer proc will be called as many times as the
destination requests, each time with a different target that was converted.
A destination must be prepared to use any of the target types it requests. If
the operation was a move, the transfer proc must add a DELETE target
when it has successfully received all of the converted data from the source.

The following example method was called from the dropProc example code in the
previous subsection. It creates a collection of CwDropTransferEntry objects using the
specified targetTypes and clientData, and starts the data transfer with these.

Chapter 7. Common Widgets 223


startTransfer: callData targetTypes: targetTypes clientData: clientData
"Called from a dropProc to start the drop transfer."
| dropTransfers |

dropTransfers := OrderedCollection new.


targetTypes do: [:target |
dropTransfers
add: (CwDropTransferEntry
target: target
transferClientData: clientData)].
callData dragContext
dropTransferStart: [:dropTransfer |
dropTransfer
dropTransfers: dropTransfers;
transferProc: (CwCallbackRec
receiver: self
selector: #transferProc:clientData:callData:
clientData: callData)].

The convert proc


The source application must supply a convert proc when it creates a drag context.
The convert proc is where the source provides the dragged data in the format
specified by its callData target type. The parameters to the convertProc are as
follows:
aCwDragContext
The CwDragContext created for the drag
clientData
The clientData specified when the convertProc was hooked
callData
An instance of CwConvertProcCallbackData

The following code shows a typical convert proc. Assume that stringFromWidget:
answers the selected string in the specified widget, and deleteStringFromWidget:
deletes the selected string from the specified widget.

Note: You can use the clientData parameter to find out the drag source widget
inside the convert proc.
convertProc: aCwDragContext clientData: dragSource callData: callData
"Handle the convert proc for the receiver."
| target |
(target := callData target) = 'STRING'
ifTrue: [
"Convert to requested type, and set value."
callData value: (self stringFromWidget: dragSource)]
ifFalse: [
target = 'DELETE'
ifTrue: [
"The operation was XmDROPMOVE, and the destination
successfully received the data. Delete the data from the source."
self deleteStringFromWidget: dragSource]].

Note: A convertProc is usually sent to the source application, and then the
corresponding transferProc is sent to the destination application. On some
platforms, the convertProc is called for all of the dragContexts export targets
when the drag starts. Thus, applications should not count on the order of
convertProc and transferProc calls.

224 IBM Smalltalk: Programmers Reference


The transfer proc
The destination application must supply a transfer proc when it creates a drop
transfer. The transfer proc is where the destination receives the dragged data from
the source in the format specified by the target type in the transfer procs callData.
The parameters to the transferProc are as follows:
aCwDropTransfer
The CwDropTransfer created for the data transfer
clientData
The clientData specified when the transferProc was hooked
callData
An instance of CwTransferProcCallbackData

The following code shows a typical transfer proc. Assume that


stringToWidget:string:x:y:link: writes a string into the drop site widget, given the
string, the coordinates of the drop, and a Boolean describing whether the drop
operation was XmDROPLINK. (XmDROPLINK is usually more meaningful for
data other than STRING). The drop site widget was passed to the proc (by the
startTransfer:targetTypes:clientData: example method) in the callDatas
transferClientData.

Note: You can use the clientData parameter to find the dropProcs callData inside the
transfer proc. The callData is usually necessary to know because it contains
the operation and the coordinates of the mouse at the drop.
transferProc: aCwDropTransfer clientData: dropProcCallData callData: callData
"Handle the convert proc for the receiver."
callData value isNil ifTrue: [
"Conversion failed. The source did not pass any data."
|self errorMessage: 'Conversion failed.'].
callData target = 'STRING'
ifTrue: [
self
stringToWidget: callData transferClientData
string: callData value
x: dropProcCallData x
y: dropProcCallData y
link: (dropProcCallData operation = XmDROPLINK)].
"If this was a move operation, and if we have just received the
last dropTransfer, then send a 'DELETE' target to the drag
source's convert proc."
(dropProcCallData operation = XmDROPMOVE
and: [callData target = aCwDropTransfer dropTransfers last target])
ifTrue: [
aCwDropTransfer
dropTransferAdd: (CwDropTransferEntry
target: 'DELETE'
transferClientData: callData transferClientData)].

The user interface process model


The Common Widgets user interface has been modeled based on the input event
processing model supported by OSF/Motif. A central event processing loop reads
events from the operating system and dispatches them to individual widgets that
process them appropriately. This event model, called polling, is the basis of most
modern graphical user interfaces (GUIs) including OSF/Motif, Microsoft Windows,
IBM OS/2 Presentation Manager, and the Apple Macintosh operating system.

Chapter 7. Common Widgets 225


In Common Widgets, the event polling loop has been implemented fully within
high-level Smalltalk code. This has a number of significant benefits to the
application programmer:
v Existing Motif application programmer knowledge is maintained, because
custom event loops can be constructed in the standard Motif style without fear
of error-causing interactions with hidden event handling mechanisms.
v Event polling occurs only at controlled points during system execution, so
application code runs at maximum speed.
v The system requires none of the cumbersome and error-prone low-level
synchronization code required when systems attempt to hide the event loop
below the control of the application programmer.
v Complex multithreaded applications can safely perform work in background
processes, while the user interface process continues to keep the user interface
responsive.

With Common Widgets, only the single Smalltalk user interface process is
permitted to dispatch events or directly perform user interface operations.
Common Widgets facilitates a proactive approach to event management, as
opposed to a defensive one. Rather than write code to defend themselves against
asynchronous user interface events, such as exposes, menu operations, or user
input, application developers control the user interface event processing. Event
processing is fully synchronous from the perspective of the user interface process,
although it can be asynchronous from the perspective of non-UI processes.

Unfortunately, with increased capability comes increased responsibility. In the case


of the polled event model, application programmers are responsible for writing
their applications in ways that allow polling to occur at frequent intervals. The
responsiveness of an application (that is, the delay between the availability of an
event and processing of the event by the application) is directly effected by the
frequency at which the application polls. Although they vary in their sensitivity to
failures, all of the GUIs mentioned above specify that frequent polling is required
to maintain application responsiveness.

Common Widgets provides support for maintaining application responsiveness


while long-running tasks execute. This support is based on the Common Process
Model together with a standard application program interface (API) for managing
the interactions between non-UI tasks and the user interface. This is discussed in
detail in the following sections. First, a system view is presented, which provides
an overview of the implementation of these mechanisms, and then an application
view is presented, which discusses how the mechanisms are used in building
applications.

The system view


In Smalltalk images that include a user interface (Common Widgets), the startUp
class (System startUpClass) is responsible for providing a polling loop for the user
interface process, an instance of UIProcess. The UIProcess sends the message
messageLoop to the startUp class to start the polling loop. The startUp class,
EsWindowSystemStartUp, implements a simple polling loop similar to the one
shown below:
messageLoop
"Run the dispatch loop."
[true] whileTrue: [
CwAppContext default readAndDispatch
ifFalse: [ CwAppContext default sleep ]]

226 IBM Smalltalk: Programmers Reference


In general, application programmers never need to modify this code because it
provides full functionality for all but the most exceptional circumstances. However,
as mentioned above, application programmers can replace this loop with their
own.

The message loop makes use of two methods defined in class CwAppContext:
readAndDispatch
Reads a single event, if one is available, from the underlying operating
system, dispatches it to the appropriate widget, and handles any callbacks
that occur. In addition, it handles any pending requests for user interface
operations by non-UI processes, as shown below. Finally, it returns true if
an event was processed and false otherwise.
sleep Checks for user interface activity, and if none, removes the UIProcess from
the ready-to-run queue. The system assumes there is user interface activity
in the following cases:
v There are events to process
v There are background user interface requests to be run
v There are work procs registered
v There are timer procs registered

As long as there is any activity in the user interface, the UIProcess will continue to
poll for events as quickly as possible. As soon as the activity stops, the UIProcess
becomes inactive and suspends. This enables any other Smalltalk processes that are
running at the the same or lower priority than the UIProcess to execute.

Because sending CwAppContext messageLoop can deactivate the UIProcess, there


must be a mechanism for reactivating it. To support this, sleep enables an
operating-system-specific mechanism that causes the private message
CwAppContext default wake to be sent when new events become available. This wake
method is also sent by all other methods that generate user interface activity,
causing the UIProcess to respond immediately.

If the underlying operating system does not provide any mechanism for triggering
user-written code when events become available, the CwAppContext can still
function by generating a Smalltalk process that wakes the UIProcess at regular
intervals. By default, this is called the CwAsyncIOProcess.

As previously mentioned, if there is no user interface activity the UIProcess is


deactivated, enabling other Smalltalk processes at the same or lower priority to
run. However, if there are no other active processes to run, a system-provided
idle process is run, which repeatedly sends the suspendSmalltalk message to the
default CwAppContext:
suspendSmalltalk
Suspends the entire IBM Smalltalk system, using an operating-system-
specific facility, until there is event activity. When the IBM Smalltalk
system is suspended it consumes little or no processor resources. Under
multitasking operating systems this enables other applications full access to
the CPU.

As soon as input is available, both the IBM Smalltalk system and the UIProcess are
reactivated, because they are higher priority than the idle process.

If the operating system does not provide a facility for suspending execution of an
application until input is available, the suspendSmalltalk method simply returns to

Chapter 7. Common Widgets 227


its sender. In this case, Common Widgets continues to run normally, but IBM
Smalltalk competes for resources with any other applications being run by the
operating system.

Note that there is an interaction between the suspendSmalltalk method and the
Smalltalk Delay class. If the idle process runs because a higher priority process has
suspended on a Delay, the system must be reactivated when the Delay expires. This
situation is handled in one of three ways depending on the capabilities of the
operating system:
v With operating systems where the Delay class uses the same mechanism that
suspendSmalltalk uses to detect input activity, the system is reactivated with no
further intervention.
v With operating systems where Delay uses a different mechanism than
suspendSmalltalk, but it is possible for a user written application to post a user
interface event, this facility is used to reactivate the system.
v If neither of the above mechanisms are available, IBM Smalltalk checks for
Delays and deactivates the system only when there are none pending.

The application programmers view


This section looks at the impact of the input polling method on application code.

When programming with OSF/Motif, or other modern window systems,


application programmers writing simple applications typically do not worry much
about the details of the input model. They simply define the the widgets that make
up their application windows, register the appropriate callbacks, or equivalent, for
the user actions of interest, and implement the code for these callbacks. Although
this is ideally true, one important aspect is sometimes not considered: because
callbacks are run by the same process that is running the user interface, all of the
code that is run by the callback (an operation) is run before the application returns
to polling. Thus, when long-running operations are run, application
responsiveness, and whole system responsiveness on some operating systems is
affected.

In Common Widgets, programmers construct applications using the standard


OSF/Motif model described above. However, additional facilities are provided to
allow long running operations to execute without impacting responsiveness.

Because IBM Smalltalk provides support for multiple, priority-scheduled threads of


control, or processes, operations that do not use user interface facilities can be run
by a separate, low priority process, called a background process. The user
interface remains responsive because it is higher priority than the background
process and is activated whenever input is available.

Unfortunately, OSF/Motif itself does not use a multithreaded model and thus it is
not possible for multiple application processes to concurrently execute user
interface code, for example, redrawing a widget, performing an individual
graphical operation, or reading an event. Some kind of synchronization is
necessary.

It is possible for each application to implement its own synchronization scheme


that prevents background processes from attempting to concurrently execute user
interface code. However, IBM Smalltalk provides standard mechanisms to support
this kind of synchronization.

228 IBM Smalltalk: Programmers Reference


Background user-interface requests
A background user interface request is a block of code that must be run by the
UIProcess at a point when no other user interface code is being run. The following
two methods are then implemented in class CwAppContext to take background user
interface requests.

Tip: A background (non-UI) process must never make widget or graphics requests
directlyit must use syncExecInUI: or asyncExecInUI: to ask the user interface
to issue the request on its behalf.

Background user interface requests are run by atomically interleaving their


execution with the event and callback processing of the user interface process.
Once execution of the block has been started, no further user interface events can
be processed until execution of the block has been completed.

asyncExecInUI

The asyncExecInUI UIProcess executes a block during readAndDispatch processing, at


the next clean point, that is, at the next point where it is not already executing
user interface code. No result is returned.

Processes with higher priority than the UIProcess do not block when this method is
run. In this case, aBlock is run the next time the UI becomes active and reaches a
clean point. Processes at the same or lower priority than the UIProcess normally
block until aBlock has been run, but this is not guaranteed.

The process that runs this message is not provided any indication of when aBlock
has been run. In particular, it is not guaranteed that aBlock has been run when the
method returns.

If this message is sent by code that is executing in the UIProcess, then aBlock is run
after all previously queued background user interface requests have been run.

Tip: A background process can reawaken the UIProcess and cause a context switch
(because the UIProcess is higher priority) by executing: CwAppContext default
asyncExecInUI: []
syncExecInUI:

The syncExecInUI: UIProcess runs a block during readAndDispatch processing, at the


next clean point, that is, at the next point where it is not already executing user
interface code. The result of evaluating aBlock is returned.

Execution of the process that evaluates this method always suspends until aBlock
has been evaluated.

If this message is sent by code that is executing in the UIProcess, the block is run
immediately.

Both of these methods are implemented to add the code to be run, aBlock, to a
collection of background user interface requests maintained by the CwAppContext.
This collection is FIFO ordered so operations can use syncExecInUI: to ensure that
all previously sent asyncExecInUI: messages have been processed.

After adding the request, both of the above methods reactivate the UIProcess if it
was sleeping. As was described in the previous section, the CwAppContext
processes the background user interface requests as part of the normal functioning

Chapter 7. Common Widgets 229


of the readAndDispatch method. Processing of pending background user interface
requests is interleaved with user interface event dispatching.

Using the above methods, it is possible for programmers to construct applications


that are both responsive and contain long running operations. The long-running
operations are run by background tasks that use asyncExecInUI: or syncExecInUI: to
access the user interface. Obviously, the programmer is responsible for ensuring
that the individual blocks that are passed to the asyncExecInUI: methods do not
take an unusually long time to execute.

Tip: Because execution of background graphics requests can be deferred while


events are processed, background user interface request blocks must be
prepared for eventualities such as widgets being destroyed between the
request being posted and run.

Work procs and timer procs


In addition to background user interface requests, there are two other mechanisms
that can be used to defer execution or have a method repeatedly executed. The
following two methods are in the class CwAppContext. Unlike the background user
interface request methods described above, work procs and timer procs can only
be registered by the UI process itself. As with background user interface requests,
work procs and timer procs are sent from the event loop during idle processing.

The processing of active work procs and timer procs prevents the user interface
process from suspending, and background processes will not run until all work
procs and timer procs have been removed. However, the enableProcs: message in
CwAppContext can be used to temporarily disable work proc and timer proc
processing in order to allow the UIProcess to suspend.

As with background user interface requests, work procs and timer procs are only
processed by the UIProcess during readAndDispatch processing. If the UIProcess is
compute bound, no work procs or timer procs will be processed until the UIProcess
returns to the event loop.

Note: Keep the amount of work done in a work proc or timer proc to a minimum.
If they are too long, they keep the UIProcess from returning to the event
loop, which can cause user interface responsiveness to degrade.
Long-running operations should be coded in background tasks that use
asyncExecInUI: or syncExecInUI: to access the user interface.

addWorkProc:receiver:selector:clientData:

This message adds a work proc for idle UI processing, and returns an object which
uniquely identifies the work proc. This object can later be used to remove the work
proc using removeWorkProc:. The 1-parameter message defined by selector is
repeatedly sent to receiver (with clientData as the parameter) while the event loop
is idle waiting for events. Multiple work procs may be added, and the last one
added is called first. If the work proc message returns true, then it is automatically
removed and will not be called again.

addTimeOut:receiver:selector:clientData:

This message adds a timer proc which is sent after the specified number of
milliseconds have elapsed, and returns an object which uniquely identifies the
timer proc. As with work procs, this object can be used later to remove the timer
proc before it expires if it is no longer needed. Multiple timer procs can be added,

230 IBM Smalltalk: Programmers Reference


but unlike work procs, timer procs are always sent only once. When the interval
expires, the 1-parameter message specified by selector is sent to receiver with
clientData as parameter.

Note: Because timer procs are only sent when the UI is idle, they may not expire
at the exact time specified. They are only guaranteed to expire after the
given number of milliseconds have passed.

The UI idle processing mechanisms are as follows:

asyncExecInUI: syncExecInUI: addWorkProc:... addTimerProc:...


Can be registered by Can be registered by Can be registered Can be registered
any process any process only by UI process only by UI process
Next pending block Block evaluated Next pending Next expired
evaluated at next UI immediately if called message sent at next message sent at next
idle from UI UI idle UI idle
processotherwise
next pending block
evaluated at next UI
idle
No return value Returns result of Returns workProc ID Returns timerProc ID
block
Block evaluated once Block evaluated once Message sent Message sent once
only only repeatedly until it only
returns true
Processed in FIFO Processed in FIFO Processed in LIFO Processed in LIFO
order order order order
Cannot be disabled Cannot be disabled Can be disabled Can be disabled

Examples of applications with long-running operations


The following are some overviews of suggested implementation styles when
building applications with long-running operations using IBM Smalltalk. These
examples are intended to show typical ways that applications can be written using
IBM Smalltalk. Of course, every application is different so developers can use any
or all of these techniques, or construct new idioms that are more appropriate to
their problem domain. The important point to remember is that, there are no
surprises. Every aspect of the polling model is accessible to the application
developer. Nothing is done under the covers.

Example 1: a simple text editor


A simple text editing application is constructed using IBM Smalltalk. The
application is responsive in all situations except while reading or writing the file
that is being edited. To maintain responsiveness in this situation, file reading and
writing are moved to a background process. The operations are modified to use a
modal percentage complete dialog that is updated by the background process
using asyncExecInUI:. When the file has been completely read or written, the
background process uses a call to syncExecInUI: to close the dialog.

Example 2: a program development environment


A program development environment is constructed that uses a database to store
source code. Saving large segments of source code is found to cause a lack of
responsiveness because several database accesses are required. To maintain
responsiveness, the saving operation is modified to:
v Disable any menus or buttons that could affect the saving operation

Chapter 7. Common Widgets 231


v Change the cursor to indicate that an operation is in progress
v Generate a background process that first saves the source to the database, and
then uses asyncExecInUI: to re-enable the menus and buttons and set the cursor
back to normal.

Example 3: a complex drawing editor


A drawing editor is constructed that allows large, complex drawings to be built.
The system is responsive in all situations except while updating a display of a
large drawing. The update display (refresh) operation is modified as follows:
v First, it checks the complexity of the drawing (for example, by detecting the total
number of primitive graphical objects that it contains) and if it is below a
particular threshold, it refreshes the drawing directly.
v If the drawing is too complex to refresh in the UIProcess, the operation changes
the cursor to indicate that a refresh operation is in progress.
v Then it generates a background process that draws the diagram, one component
at a time, using a background user interface request for each component. After
the diagram is redrawn, the application sets the cursor back to its standard
shape using a synchronous background user interface request. The application
retains a reference to the background process in order to terminate it if the user
performs some action that invalidates its usefulness (for example, resizing or
closing the window, or using a menu entry to request another refresh or switch
to a different drawing).

Notice that in this example, the same code could be used to do the actual
refreshing of the drawing, regardless of whether it is being run by the UIProcess or
by a background process, because the ExecInUI methods can always be called by
both background processes and the UIProcess.

232 IBM Smalltalk: Programmers Reference


Chapter 8. Extended Widgets
This chapter describes the Extended Widgets (EW) subsystem. The Extended
Widgets classes and methods enable application developers to expand upon the
graphical user interfaces built using the Common Widgets (CW) subsystem.
Common Widgets provides a framework for developing custom widgets. The
family of Extended Widgets discussed in this document was developed using this
framework. For a description of the CW subsystem API and the extended widget
framework, refer to Chapter 7. Common Widgets on page 135.

Extended Widgets class hierarchy


The following table describes the class hierarchy of extended widgets and class
responsibilities.
Table 38. Widgets hierarchy
Class hierarchy Responsibility
CwWidget Defines common behavior for all widgets
CwExtendedWidget Defines common behavior for all extended widgets
CwExtendedComposite Defines common behavior for all extended composites
EwNotebook Abstract superclass for notebook widgets
EwPMNotebook Displays a OS/2 look and feel notebook
EwWINNotebook Displays a Windows look and feel notebook
EwPage Defines a composite that can be added to notebooks
EwSplitWindow Splits a composite into panes and provides bars for
allowing the user to resize the panes
EwToolbar Displays and lays out rows of tools
CwExtendedPrimitive Defines common behaviors for all extended primitives
EwScrollable Abstract superclass for scrollable widgets
EwList Abstract superclass for list widgets
EwIconArea Displays list of items in free-form style
EwLinearList Abstract superclass for linear lists of items
EwContainerList Abstract superclass for lists that are container-like views
EwIconList Displays single-column list of items in icon and label
format
EwFlowedIconList Displays multi-column list of items in icon and label
format
EwIconTree Displays hierarchical single-column list of items
EwTableList Displays multi-column list of items in tabular format
EwTableTree Displays hierarchical list of items in tabular format
EwDrawnList Displays a list with application-drawn items
EwSlider Displays an analog representation of a range of values
EwSpinButton Displays an entry field with two arrow buttons for cycling
through a range of values.

EwConstants pool dictionary


The Extended Widgets subsystem uses a pool dictionary called EwConstants to
provide pool variables for constant values that are used as arguments to Extended
Widgets methods. These pool variable names should be used instead of directly
using their constant values. Classes that require pool variable names must include
the EwConstants pool dictionary in their class definition.

Copyright IBM Corp. 1994, 2000 233


Many constants are also defined in the CwConstants pool dictionary. The
CwConstants pool dictionary should also be included in the class definition of
classes that require these pool variable names.

Creation convenience methods


Extended widgets are created using convenience functions implemented as
instance methods of CwWidget. Convenience messages are sent to the widget that is
to be the new widgets parent. Following is a list of extended widget creation
convenience methods:
v createDrawnList:argBlock:
v createFlowedIconList:argBlock:
v createIconArea:argBlock:
v createIconList:argBlock:
v createIconTree:argBlock:
v createPMNotebook:argBlock:
v createProgressBar:argBlock:
v createScrolledDrawnList:argBlock:
v createScrolledFlowedIconList:argBlock:
v createScrolledIconArea:argBlock:
v createScrolledIconList:argBlock:
v createScrolledIconTree:argBlock:
v createScrolledTableList:argBlock:
v createScrolledTableTree:argBlock:
v createSlider:argBlock:
v createSpinButton:argBlock:
v createSplitWindow:argBlock:
v createTableList:argBlock:
v createTableTree:argBlock:
v createToolBar:argBlock:
v createWINNotebook:argBlock:

Extended list widgets


The Common Widgets subsystem provides the CwList widget so that applications
can display lists of strings that can be viewed, selected, and scrolled. Because it is
often more convenient to interact with and display collections of random objects,
the Extended Widgets subsystem allows applications to create container widgets
that can contain arbitrary objects and provide visual presentations other than
simple text. Some of these widgets have a hierarchical (tree) version that displays
hierarchical lists of objects.

The extended container widgets use different techniques for drawing the items
provided by applications:
v EwDrawnList uses callbacks to request customized drawing of items by an
application.
v EwIconList, EwIconTree, EwFlowedIconList, and EwIconArea use callbacks to request
icons and labels that represent each item from an application.
v EwTableList and EwTableTree use callbacks to request the objects to be displayed
in individual cells.

234 IBM Smalltalk: Programmers Reference


Common resources
All extended container widgets support the items resource. The items resource
represents the collection of objects to be displayed in a list. The widgets also
provide itemCount and selectionPolicy resources. These are similar to the resources
of the same name in the common widgets CwList. All extended container widgets
support using an OrderedCollection for the items resource. Some of the widgets are
more flexible in the value type for the items resource. Consult the definition of the
items resource in a particular widget to see what type of value is required.

Tip: Most of the extended list widgets (all except for EwlconArea) support ordered
lists. The lists are protocol compatible with CwList. Items can be added,
deleted, replaced, or selected in the items list using standard CwList protocol.

Scrolled lists
Each of the container widgets can be created with scrolling capability, much like
CwList. All resources and callbacks associated with a particular list can be applied
to the scrolled version of that list. Scrolling is handled automatically. The
scrollHorizontal resource specifies whether the widget should provide a horizontal
scroll bar.

Tip: The horizontal scroll bar will not be shown in the list widget unless it has
been created as a scrolled list, even if the scrollHorizontal resource is set to
true.

The scrollBarDisplayPolicy resource controls the automatic placement of the scroll


bars. If the resource is set to XmASNEEDED, scroll bars are displayed if the list
items exceed the list work region in one or both dimensions. If the resource is set
to XmSTATIC, scroll bars are displayed regardless of the relationship between the
size of the list items and the list work region.

In the following example, a scrolled EwIconList is created as a child of a form.


Creating a scrolled list inserts a CwScrolledWindow parent between the list and the
receiver of the creation message, in this case, the form. It is important to
understand this distinction when attaching a scrolled list to a form or attaching
other form children to the list.
| shell form iconList |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Scrolling List Example'].
form := shell
createForm: 'form'
argBlock: nil.
form manageChild.
iconList := form
createScrolledIconList: 'scrolled list'
argBlock: [ :w | w
items: self itemList;
scrollHorizontal: true].
iconList manageChild.
"Note: The icon list's parent is sent attachment messages."
iconList parent
setValuesBlock: [:w | w
topAttachment: XmATTACHFORM;
rightAttachment: XmATTACHFORM;
leftAttachment: XmATTACHFORM;
bottomAttachment: XmATTACHFORM].
shell realizeWidget.

Chapter 8. Extended Widgets 235


Tip: When a scrolled list is created, the creation message returns an instance of the
list (the child of a CwScrolledWindow). However, because the CwScrolledWindow
is itself the child of the form, form attachment messages must be sent to the
lists parent (that is, to the CwScrolledWindow). Any other form children
attaching to the scrolled list should be attached to the lists parent.

Drawn list widget


The application-drawn list widget (EwDrawnList) allows an application to draw
arbitrary graphics to represent each object in the list. It combines list behavior with
a drawing area-based widget. Following is an example of a drawn list widget:

An application hooks the displayCallback to draw the items in the list. If the items
in the list have different sizes, an application should hook the measureCallback to
specify the height of each individual item in the list. If all items have the same
height, the itemHeight resource can be used to specify the height in pixels.

The applicationDrawnStates resource allows for the specification of visuals for any of
the emphasis states in the list, such as selection emphasis or cursored emphasis.
Applications can choose to allow the drawn list to provide these emphasis visuals.
In the following code, a list of CgFontStructs is added to a drawn list, and each
font name in the list is drawn with the font that it describes.
Object subclass: #DrawnListExample
instanceVariableNames: 'fontStructs'
classVariableNames: "
poolDictionaries: 'CgConstants CwConstants EwConstants'
open
| shell drawnList |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Drawn List Example'].
fontStructs := ((CgDisplay default listFonts: '*' maxnames: 100)
collect: [:fontName |
CgDisplay default loadQueryFont: fontName]) asOrderedCollection.
drawnList := shell
createScrolledDrawnList: 'drawnList'
argBlock: [ :w | w items: fontStructs].
drawnList
addCallback: XmNdisplayCallback
receiver: self
selector: #display:clientData:callData:
clientData: nil;

236 IBM Smalltalk: Programmers Reference


addCallback: XmNmeasureCallback
receiver: self
selector: #measure:clientData:callData:
clientData: nil;

addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.

drawnList manageChild.
shell realizeWidget.
display: widget clientData: clientData callData: callData
"Display the fontStruct by drawing its font name in the
upper left-hand corner of the specified rectangle, using
the fontStruct as the font."
| fontStruct |
fontStruct := callData object.
callData gc setFont: fontStruct font.
callData drawable
drawString: callData gc
x: callData x
y: callData y + fontStruct ascent
string: fontStruct name
measure: widget clientData: clientData callData: callData
"Measure the fontStruct by querying its height."
| fontStruct |
fontStruct := callData object.
callData height: fontStruct height
destroy: widget clientData: clientData callData: callData
"Destroy the fontStruct collection."

fontStructs do: [:fontStruct | fontStruct freeFont].

Icon list widgets


An icon list widget (EwIconList) displays each of the items in its list as a labelled
icon. Following is an example of an icon list widget:

The application hooks the visualInfoCallback to specify the label and icon for each
object in the list. Typically, an application provides both an icon and a string in this
callback. However, either value can be set to nil if only the icon or label is desired.

Tip: If your application requires a view with either an icon or label per item, but
not both, set the icon to nil, and the label to the desired visual object for each
item in the visualInfoCallback callData. Set the labelOrientation resource of the
widget to XmRIGHT (default), and set the emphasisPolicy resource of the
widget to XmTOGETHER.

Chapter 8. Extended Widgets 237


In the following example, a list of classes is added to an icon list. The class name is
used as the label of each item, and each item in the list displays the same icon.
Object subclass: #IconListExample
instanceVariableNames: 'icon'
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants'
open
| iconList shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Icon List Example'].
iconList := shell
createScrolledIconList: 'iconList'
argBlock: [ :w | w items: Object subclasses].
iconList
addCallback: XmNvisualInfoCallback
receiver: self
selector: #visualInfo:clientData:callData:
clientData: nil.
shell
addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.
icon := shell screen
getIcon: 'default_xm_information' foregroundColor: CgRGBColor black.

iconList manageChild.
shell realizeWidget.
visualInfo: widget clientData: clientData callData: callData
"Provide the icon and label for the class contained in the callData."

callData
label: callData item name;
icon: icon
destroy: shellWidget clientData: clientData callData: callData
"Free the icon resource."

shellWidget screen destroyIcon: icon

Renderables
In the Common Widgets subsystem, widgets require applications to provide a
strongly typed visual object. For example, labels are required to be an instance of
String, and images must be instances of CgIcon or CgPixmap. In the Extended
Widgets subsystem, the visual object types are less restrictive. Most extended
widget label, icon, or image resources require only that the supplied visual object
conform to a specified rendering protocol. Any object that conforms to this
protocol and can display (render) itself is called a renderable.

To be renderable, an object needs to implement the following messages to provide


sizing information and perform drawing operations: ewWidthUsing:,
ewHeightUsing:, and ewDrawUsing:. The classes CgDeviceIndependentImage, CgIcon,
CgPixmap, Character, Object, String, and UndefinedObject have been extended to
provide rendering protocol. Instances of these classes can be supplied for any
extended widget resource requiring a renderable.

You can add rendering protocol to other visual objects in your application. For
example, a colored string renderable can be made by combining a String and a
CgRGBColor into a ColoredString object.

238 IBM Smalltalk: Programmers Reference


Tip: When you are prototyping, the overhead of creating icons can be an
inconvenience. Because any renderable can be used in the container widgets it
is sometimes useful to use a String in place of a CgIcon.

Direct editing of labels


The icon widgets (EwIconList, EwFlowedIconList, EwIconArea, and EwIconTree) can be
configured to allow dynamic editing of an items label. The application must
determine what user action is to trigger the editing of a label. After this action is
detected, the application should request editing of a specific item.

When an edit action is requested, the widget fires the beginEditCallback to


determine the specific editing policy (see Edit policies on page 246). When
editing is complete, the widget activates the endEditCallback to allow the
application to save the edited value as appropriate.

The following example enables label editing in an icon list. The


defaultActionCallback is used to trigger editing of the label.
Object subclass: #IconListEditingExample
instanceVariableNames: 'icon'
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants'
open
| iconList shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Icon List Example'].
iconList := shell
createScrolledIconList: 'iconList'
argBlock: [ :w | w
items: #('first item' 'second item' 'third item' 'fourth item')
asOrderedCollection].
iconList
addCallback: XmNvisualInfoCallback
receiver: self
selector: #visualInfo:clientData:callData:
clientData: nil;

addCallback: XmNdefaultActionCallback
receiver: self
selector: #defaultAction:clientData:callData:
clientData: nil;
addCallback: XmNbeginEditCallback
receiver: self
selector: #beginEdit:clientData:callData:
clientData: nil;

addCallback: XmNendEditCallback
receiver: self
selector: #endEdit:clientData:callData:
clientData: nil.
icon := shell screen
getIcon: 'default_xm_information' foregroundColor: CgRGBColor black.
shell
addCallback: XmNdestroyCallback
receiver: self
selector: #destroy:clientData:callData:
clientData: nil.

iconList manageChild.
shell realizeWidget.

Chapter 8. Extended Widgets 239


defaultAction: widget clientData: clientData callData: callData
"Request editing of the selected object."
widget editItemAt: callData itemPosition
beginEdit: widget clientData: clientData callData: callData
"Initiate the edit for the specified item."

callData doit: true


endEdit: widget clientData: clientData callData: callData
"End the edit for the specified item. Replace the old text
with the new text."

widget
replaceItems: (Array with: callData item)
newItems: (Array with: callData newValue)
visualInfo: widget clientData: clientData callData: callData
"Provide the icon and label for the class contained in the callData."
callData
icon: icon;
label: callData item.
destroy: shellWidget clientData: clientData callData: callData
"Free the icon resource."

shellWidget screen destroyIcon: icon

Flowed icon list widget


A flowed icon list widget (EwFlowedIconList) provides a vertical, multi-column list
of items with an icon and a label for each item. Items are arranged in rows with as
many items per row as can fit in the available width. Each items width and height
are determined by the itemWidth and itemHeight resources. Thus, the dimensions of
the rows and columns are fixed. Following is an example of a flowed icon list
widget:

The icons and labels an application supplies in the visualInfoCallback can be any
renderable or nil (see Renderables on page 238). This is the same as EwIconList.

The following example shows the creation of a flowed icon list (the
visualInfoCallback is not shown).
| flowedIconList shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Flowed Icon List Example'].
flowedIconList := shell
createScrolledFlowedIconList: 'flowedIconList'
argBlock: [ :w | w
items: Object subclasses;
itemWidth: 200;
itemHeight: 36].
flowedIconList manageChild.
shell realizeWidget.

240 IBM Smalltalk: Programmers Reference


Icon area widget
The icon area widget (EwIconArea) provides a free-form display of objects in a list.
The widget displays a text label and icon for each item in the list, using the same
technique as with EwIconList to supply icons and labels. Following is an example
of an icon area widget:

Each item in the list is associated with a location, which is a Point representing
where the item is displayed in the icon area. Items can be added to the list at a
specific location using the addItem:location: or addedItem:location: protocol.

Items in an icon area can be rearranged so that no items overlap using the
arrangeItems function. The icons and labels an application supplies in the
visualInfoCallback can be renderable (see Renderables on page 238). This is the
same as EwIconList. The following example creates an icon area and adds an item
at a specific point (the visualInfoCallback is not shown).
| iconArea shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Icon Area Example'].
iconArea := shell
createScrolledIconArea: 'iconArea'
argBlock: nil.
iconArea manageChild.

shell realizeWidget.
iconArea addItem: 'Fred' location: 25@30.

Table list widget


The table list widget (EwTableList) displays various attributes for each item in the
list. Each item is displayed in its own row of the table. An application can specify
the displayed attributes for each item by defining columns and adding them to the
table. The value of a specific items attribute is contained in a cell. A specific cell
can be described by specifying the row in which a particular item is displayed and
the column in which a particular attribute value is displayed. Following is an

Chapter 8. Extended Widgets 241


example of a table list widget:

Tip: The objects used for cell values and headings are typically Strings, Numbers,
and CgIcons, but this is not a requirement. It is possible for an application to
use any renderable object as the cell value or heading (see Renderables on
page 238).

Table list resources


To use all of a table lists features, an application must set resources and callbacks
for the table list widget and for its individual columns.

The columns of the table list widget are defined by the columns resource that
should be set to a collection of EwTableColumn instances. The resources that can be
set for each column are described in Table columns on page 243.

The table list widget supports the standard selection policies, plus additional
policies for cell selection.
Standard selection policies
Additional policies for cell selection
XmSINGLESELECT
Standard selection policies apply to the rows in the table as a whole
XmEXTENDEDSELECT
Standard selection policies apply to the rows in the table as a whole
XmMULTIPLESELECT
Standard selection policies apply to the rows in the table as a whole
XmBROWSESELECT
Standard selection policies apply to the rows in the table as a whole
XmCELLSINGLESELECT
Allows individual cells to be selected
XmCELLBLOCKSELECT
Allows contiguous blocks of rows to be selected

The table list selection policies involve a separate protocol. Messages such as
selectPos:notify: and deselectAllItems are used with the standard policies. Messages
such as selectCell:notify: and deselectCell: are used with the cell selection policies. It
is important to ensure that the protocol being used at any given time is

242 IBM Smalltalk: Programmers Reference


appropriate for the widgets selection policy. Row selection methods should not be
used with cell selection policies and vice versa.

Many resources affect the visual appearance of the table:


visualStyle
Specifies whether the rows or individual cells should have an etched, three
dimensional appearance
headingSeparatorThickness
Controls the appearance of the line between column headings and the
rows of cell values
headingVisualStyle
Specifies whether the column heading should have an etched, three
dimensional appearance
rowSeparators
Specifies whether lines should be drawn between each row
separatorsToExtremes
Specifies whether row separators should stop at the last column or
continue to the border of the widget
lockedColumns
Specifies the number of columns that should be locked on the left-hand
side. When a column is locked, it does not scroll when the list is scrolled
horizontally. This allows certain anchor columns to always remain visible,
such as an icon or name column.
editable
Specifies whether the cell values in the table can be edited. This can be
controlled for each individual column. Editing is discussed in Direct
editing of cell values on page 245 and Edit policies on page 246.

Table columns
The EwTableColumn objects in a table list have their own set of resources and
callbacks. The cellValueCallbackmethod obtains the renderable object that represents
the value of a column in a particular row.

Additional resources control the appearance of the column and its heading. The
heading resource can be set to a renderable object that is to be displayed at the top
of a particular column. The resizable resource controls whether users can resize the
column to a different width by dragging the right side of the column heading.
Other resources control the size, alignment, and visual style of the column.

The following code creates a table list with the subclasses of the class Object as its
items. The first column displays an icon, the second displays the name of the class,
and the third displays the classs comment.
Object subclass: #TableListExample
instanceVariableNames: 'icon'
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants '
open
| shell tableList |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Table List Example'].

Chapter 8. Extended Widgets 243


tableList := shell
createScrolledTableList: 'scrolled list'
argBlock: [:w | w
columns: self columns;
items: Object subclasses;
selectionPolicy: XmSINGLESELECT].
"Note: The destroyCallback is not shown."
icon := shell screen
getIcon: 'default_xm_information' foregroundColor: CgRGBColor black.

tableList manageChild.
shell realizeWidget
columns
"Answer a collection of EwTableColumns."
| OrderedCollection new
add: self iconColumn;
add: self nameColumn;
add: self commentColumn;
yourself
iconColumn
"Answer the column for the class' icon."
| col |
col := EwTableColumn new
editable: false;
resizable: false;
width: 36;
horizontalAlignment: XmALIGNMENTCENTER;
yourself.
col
addCallback: XmNcellValueCallback
receiver: self
selector: #iconCellValue:clientData:callData:
clientData: nil.
|col
nameColumn
"Answer the column for the class' name."
| col |
col := EwTableColumn new
editable: false;
heading: 'Class Name';
resizable: true;
width: (CgFontStruct default textWidth: 'Class Name');
horizontalHeadingAlignment: XmALIGNMENTCENTER;
yourself.
col
addCallback: XmNcellValueCallback
receiver: self
selector: #textCellValue:clientData:callData:
clientData: #name.
| col
commentColumn
"Answer the column for the class' comment."
| col |
col := EwTableColumn new
editable: false;
heading: 'Comment';
resizable: true;
width: 512;
horizontalHeadingAlignment: XmALIGNMENTCENTER;
yourself.
col
addCallback: XmNcellValueCallback
receiver: self
selector: #textCellValue:clientData:callData:
clientData: #comment.
| col

244 IBM Smalltalk: Programmers Reference


iconCellValue: widget clientData: clientData callData: callData
"Set the cell's value to the an icon."

callData value: icon


textCellValue: widget clientData: clientData callData: callData
"Set the cell's value to the string answered by the list
item when the clientData is sent to it as a message."

callData value: (callData item perform: clientData)

Direct editing of cell values


The application programming interface (API) for user editing of cell values is
similar to the interface provided by EwIconList. The main difference with a table
list widget is that the beginEditCallback and endEditCallback resources are used to
provide editing facilities for individual cell values rather than for items in the list.
Cell editing can take place automatically as individual cells are selected, or it can
occur under application control by using editing functions.

Cell editing can automatically begin when a user clicks on a cell. To enable
automatic cell editing the following resources must be set:
v The editable resource of the table list widget must be set to true.
v The editable resource of each column in which cells are to be edited must be set
to true.
v The selectionPolicy resource of the table list must be set to
XmCELLSINGLESELECT or XmCELLBLOCKSELECT.

When cell editing is under application control, editing begins when the application
sends editCellAt: or editSelectedCell to the table list widget. These functions can be
used to trigger cell editing regardless of the value of the editable resource in the
table or in its columns.

In both automatic editing and application-controlled editing, an application must


specify a callback handler for the beginEditCallback and endEditCallback resources for
each column that is to provide editing capability. When a cell is about to be edited,
either because you clicked in it or because the application sent editCellAt: or
editSelectedCell, the column activates its beginEditCallback. The application must
hook this callback for editing to occur. At a minimum, the application must set the
callData doit flag to true to allow editing to begin.

Editing ends when the widget loses focus or another item is selected. When this
happens, or when the value in the edit widget has been changed (the exact details
of when a change has occurred depend on the edit policy; see Edit policies on
page 246), the column activates its endEditCallback. The callData includes the old
value and the new value. Your application should hook this callback and save the
new edited value in your application as appropriate. The cell is then automatically
refreshed after the callback is sent, so that the new value is obtained and
displayed.

The following code builds on the previous example and shows how cell editing
can be provided. First, the table list must be created in a slightly different way:
...
tableList := shell
createScrolledTableList: 'scrolled list'
argBlock: [:w | w
columns: self columns;
items: Object subclasses;

Chapter 8. Extended Widgets 245


"Automatic editing is provided by setting the table list to be
editable and setting the selection policy to XmCELLSINGLESELECT."
editable: true;
selectionPolicy: XmCELLSINGLESELECT].
tableList manageChild.
...

Next, the comment columns beginEditCallback and the endEditCallback must be


hooked, and the editable resource must be set to true:
commentColumn
"Answer the column for the class' comment."
| col |
col := EwTableColumn new
editable: true;
heading: 'Comment';
resizable: true;
width: 512;
horizontalHeadingAlignment: XmALIGNMENTCENTER;
yourself.
col
addCallback: XmNcellValueCallback
receiver: self
selector: #textCellValue:clientData:callData:
clientData: #comment;

addCallback: XmNbeginEditCallback
receiver: self
selector: #beginEditText:clientData:callData:
clientData: nil;

addCallback: XmNendEditCallback
receiver: self
selector: #endEditText:clientData:callData:
clientData: nil.
| col

Finally, the handlers must be written for the beginEditCallback and the
endEditCallback.
beginEditText: widget clientData: clientData callData: callData
"About to start editing a text field. Ensure the doit field is true."

callData doit: true

endEditText: widget clientData: clientData callData: callData


"Editing of a cell has just completed. Accept the value
and place it into the appropriate place in the list item."

callData item comment: callData newValue

Edit policies
For simple text editing of cell values, an application need only provide simple
beginEditCallback and endEditCallback handlers. In some advanced applications,
simple text editing of a cell might not be sufficient. In these situations, the
application can specify an edit policy in the callData of the beginEditCallback.

The edit policy defines the type of widget to be used for editing and some other
edit semantics. The default edit policy is EwTextEditPolicy set up to use a
single-line CwText as the edit widget. The application can substitute a more
appropriate edit policy. For example, if the cell contains a day of the week, the
application might wish to use an EwComboBoxEditPolicy. Applications can define

246 IBM Smalltalk: Programmers Reference


custom edit policies by subclassing EwEditPolicy as required. The supplied
subclasses of EwEditPolicy serve as good examples of this.

The following edit policies are provided in the Extended Widgets subsystem:
EwComboBoxEditPolicy
A cell is edited using a combo box. An application can specify the list of
items to choose from and the initial value. It can also specify whether users
can edit the value manually or just choose one of the items in the list.
EwTextEditPolicy
A cell is edited using a text widget. An application can specify whether the
widget is to be single or multiple lines, what the initial text value should
be, what the initially selected text should be, and what the maximum
length should be.
EwToggleButtonEditPolicy
A cell is edited using a toggle button. An application can specify the label
string for the toggle button, the horizontal alignment of the label string
and whether or not the toggle should be initially selected.

Note: The EwTextEditPolicy and EwComboBoxEditPolicy cancel an edit operation


when the ESC key is pressed. When an edit operation is cancelled, the
endEditCallback is activated using the items old value as the edited value.

Tree widgets
Tree widgets (EwIconTree and EwTableTree) provide a hierarchical view of the items
in a list. Only the top-level items of a hierarchy (the roots) are set in the items
resource of a tree widget. The childrenCallback must be hooked so an application
can provide a collection of children for any of the items in the list. The
visualInfoCallback must be hooked so an application can set the hasChildren flag in
the callData to indicate whether the item to be displayed has any children. This
allows the tree widget to provide any visuals necessary to indicate that the item
has children.

Tip: The childrenCallback is activated only for items in the list that return true as
the value of the hasChildren flag of the visualInfoCallback callData.

Tree widgets provide protocol that allows applications to control that branches of
the tree are displayed. By default, only the roots of a tree are shown initially.
Methods, such as expandPos:notify:, collapsePos:notify:, and expandCollapsePos:notify:,
allow an application to expand or collapse branches of the tree.

The visual appearance of the tree depends upon the value of the hierarchyPolicy
resource. This resource allows applications to specify the amount of indentation
that should be used to distinguish levels of a hierarchy. It also allows the
specification of specialized hierarchy graphics, such as lines drawn to connect the
items, or the button to be provided next to items that have children. By default, the
hierarchyPolicy of tree widgets is an instance of EwHierarchyPolicy, with the lines
flag set to true (indicating that lines should be drawn to illustrate the hierarchy
when expanded).

Another hierarchyPolicy class is provided, named EwIconHierarchyPolicy. This policy


shows an icon beside each item that acts as the expand and collapse button. To
allow for proper animation of pressing the button, an application can specify a
different icon to be used in each phase of the expand and collapse action.

Chapter 8. Extended Widgets 247


Tip: If the application allows hierarchy navigation using double-click actions, then
it must hook the defaultActionCallback to trigger expansion or collapse of the
hierarchy. The icon hierarchy policy automatically expands and collapses
when the user clicks on the icon.

Following are icon tree widgets with the default hierarchy policy and an icon
hierarchy policy:

Icon trees
In the following example, an icon tree is created that displays the Smalltalk class
hierarchy. The defaultActionCallback is hooked to allow navigation through the tree.
Object subclass: #IconTreeExample
instanceVariableNames: 'icon'
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants'
open
| shell iconTree |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Icon Tree Example'].
iconTree := shell
createScrolledIconTree: 'iconTree'
argBlock: [ :w | w
items: (Array with: Object)].
iconTree
addCallback: XmNvisualInfoCallback
receiver: self
selector: #visualInfo:clientData:callData:
clientData: nil;
addCallback: XmNchildrenCallback
receiver: self
selector: #children:clientData:callData:
clientData: nil;
addCallback: XmNdefaultActionCallback
receiver: self
selector: #defaultAction:clientData:callData:
clientData: nil.
"Note: The destroyCallback is not shown."
icon := shell screen
getIcon: 'default_xm_information' foregroundColor: CgRGBColor black.
iconTree manageChild.
shell realizeWidget.

248 IBM Smalltalk: Programmers Reference


children: widget clientData: clientData callData: callData
"Provide the children of the object contained in the callData."

callData children: callData item subclasses.


defaultAction: widget clientData: clientData callData: callData
"Toggle the expand and collapse state of the item in the callData."

widget
expandCollapsePos: callData itemPosition
notify: false
visualInfo: widget clientData: clientData callData: callData
"Provide the icon and label for the class contained in the callData.
Also check whether the item has children."

callData
icon: icon;
label: callData item name;
hasChildren: callData item subclasses notEmpty

Table trees
The table tree widget (EwTableTree) is a combination of the table list widget and
icon tree widget. Like the table list widget, it displays attributes of items in
columnar format. Like the icon tree widget, it also provides a hierarchical view of
the items. The columns and their callbacks are created and set up in the same way
as they are for the table list widget.

The following example creates a scrolled table tree widget. The column definitions
and column callbacks are the same as the EwTableList example. The
defaultActionCallback and childrenCallback methods are hooked and handled the
same as in the EwIconTree example. The visualInfoCallback must be hooked for a
table tree. Athough the icon and label information are not specified (they are
specified as cell values), the hasChildren flag must be set.

Tip: The first column of the table tree is used to show the hierarchy (typically, this
column shows icons). Because child items are indented, this column should be
defined with a larger width than a similar column for a table list widget.
Also, it is a good idea to define this column as resizable.
Object subclass: #TableTreeExample
instanceVariableNames: "
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants'
open
| shell tableList |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w title: 'Table Tree Example'].
tableList := shell
createScrolledTableTree: 'scrolled table tree'
argBlock: [:w | w
columns: self columns;
items: (Array with: Object);
selectionPolicy: XmCELLSINGLESELECT].
...
"Callbacks are hooked as in previous examples"
...
visualInfo: widget clientData: clientData callData: callData
"Specify whether the item has children. All other values are
provided using cell value callbacks."

callData hasChildren: callData item subclasses notEmpty

Chapter 8. Extended Widgets 249


Notebook widgets
The two notebook widgets (EwPMNotebook and EwWINNotebook) provide
user-interface elements that resemble the OS/2 notebook or the Windows tabbed
dialog control. Each page of a notebook is a composite widget and can contain an
arbitrary collection of child widgets. Both of these controls emulate existing
platform-specific controls and are available on all platforms.

Creating pages
By themselves, notebook widgets do not offer much of an interface to users. To
present a useful interface to users, notebooks must contain pages (EwPage). Pages
are created by sending one of the following messages to either a PM or WIN
notebook:
createPage:argBlock:
Creates a new page within the receiver and adds it to the end of the
receivers collection of pages. The first argument is the widget name for the
new page, the second is its argument block.
createPage:argBlock:before:
Creates a new page within the receiver before an existing page within the
receiver. The last argument is the page that the new page is to precede.
createPage:argBlock:sharingWith:
Creates a new page that shares widgets with another page in the receiver.
The last argument is the page that defines the widgets shared by itself and
the new page.
createPage:argBlock:before:sharingWith:
Creates a new page within the receiver that shares widgets with another
page. The new page is inserted before a page already within the receiver.

The following expression adds a page with a tab labeled Top Page to a notebook:
topPage := notebook
createPage: 'envinfo_page'
argBlock: [:w |
"Note: The tab label can be any renderable."
w tabLabel: 'Top Page'].
topPage manageChild.

Use createPage:argBlock:sharingWith: if many pages of a notebook are to display


different information in the same way; that is, each page will contain the same
widgets but with different information (for example, an address book). This will
save both the number of widgets needed and the speed of opening a notebook.

Tip: Pages only appear in the notebook if they are managed. To temporarily hide a
page in a notebook without destroying the page widget, use the
unmanageChild message.

Callbacks
The notebook and page widgets provide three different callbacks
(pageChangeCallback, pageEnterCallback, and pageLeaveCallback) when a new page is
brought to the top. The notebooks pageChangeCallback and a pages
pageLeaveCallback both allow, by setting a doit flag to false, an application to stop a
new page from being topped. This is useful when some input is still required from
the user on the old page. If the previous callbacks have been activated successfully,
the pages pageEnterCallback will fire. This notifies an application of the new page
being brought on top. By hooking this callback, an application can delay the

250 IBM Smalltalk: Programmers Reference


creation of the children of a page (its widgets) until that page is brought on top for
the first time. Delayed pages can be useful when a notebook has many pages or
initial open time is critical.

PM notebook widget
The PM notebook widget (EwPMNotebook) is used to create notebooks with the
look and feel of the OS/2 notebook control. By manipulating the values of a few
resources, a PM notebook widget can be made to adopt the range of looks
available from the native PM notebook control. Following is an example of a PM
notebook widget:

A notebooks image can be oriented so that the pages appear to turn vertically, like
a flip chart, or horizontally, like a book. If the value of the orientation resource is set
to XmHORIZONTAL, the binding will be drawn along the left side. If it is set to
XmVERTICAL, the binding will appear on the top or bottom depending on the
value of the backPagePosition resource.

A PM notebook is displayed as a graphic representation of a stack of pages. Images


of page edges can be drawn so that later pages are above and to the right, or
below and to the right of previous pages. This behavior is controlled by the value
of the backPagePosition resource. It can be set to XmTOPRIGHT or
XmBOTTOMRIGHT.

PM notebooks distinguish between different kinds of pages, called major and


minor pages. When a page is created, its tabType resource specifies whether it is a
major page (XmMAJOR), minor page (XmMINOR), or neither (XmNONE). The
tabs for major pages are always displayed. A minor page is associated with the
major page that precedes it. It only appears when its major page is on the top of
the notebook. Pages that are not major or minor pages do not have displayable
tabs. They can only be reached by using page buttons to traverse all of the pages.

The combination of orientation and backPagePosition resources determines how


major and minor tabs are positioned. Major tabs are always drawn opposite the
binding. Minor tabs, which only appear when the major tab they follow is brought
to the top, are drawn on the remaining page side.

The value of the bindingType resource determines what type of binding is drawn
along the bound edge of the notebooks collection of pages. Options include no
binding (XmNONE), a spiral binding (XmSPIRAL), or a solid binding (XmSOLID).

Chapter 8. Extended Widgets 251


The following example creates a PM notebook with several pages, each of which
lists the messages understood by a class. A single base page is created that shares
its widgets with the remaining pages.
Object subclass: #PMNotebookExample
instanceVariableNames: 'list '
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants '
open
"Create the necessary widgets."
| shell notebook |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w
title: 'Notebook Example';
width: 512;
height: 384].
notebook := shell
createPMNotebook: 'notebook'
argBlock: [:w | w
bindingType: XmSPIRAL;
orientation: XmVERTICAL;
backPagePosition: XmBOTTOMRIGHT;
majorTabWidth: (CgFontStruct default textWidth: 'ClassDescription')].
notebook manageChild.
self createPagesOn: notebook.
shell realizeWidget
createPagesOn: aNotebook
"Create some pages on the given notebook."
| basePage |

"Create the page that will define the widgets used by all pages."
basePage := aNotebook
createPage: 'basePage'
argBlock: [:w | w tabLabel: 'Object'].
basePage manageChild.
basePage
addCallback: XmNpageEnterCallback
receiver: self
selector: #pageEnter:clientData:callData:
clientData: 'Object'.
list := basePage
createList: 'list'
argBlock: [:w | w
topAttachment: XmATTACHFORM;
rightAttachment: XmATTACHFORM;
leftAttachment: XmATTACHFORM;
bottomAttachment: XmATTACHFORM].
list manageChild.
"Create the rest of the pages."
#('Behavior' 'ClassDescription' 'Class' 'Metaclass') do: [:name |
| aPage |
aPage := aNotebook
createPage: name
argBlock: [:w | w tabLabel: name]
sharingWith: basePage.
aPage
addCallback: XmNpageEnterCallback
receiver: self
selector: #pageEnter:clientData:callData:
clientData: name;
manageChild]

252 IBM Smalltalk: Programmers Reference


pageEnter: widget clientData: clientData callData: callData
"A notebook page has just been brought to the front. Fill in its values."

list items:
((Smalltalk at: clientData asGlobalKey) selectors collect: [
:s | s asString])

WIN notebook widget


The Microsoft Windows equivalent of a notebook control is a tabbed dialog.
Instances of the class EwWINNotebook are used to create widgets resembling
Windows tabbed dialogs. Construction of notebooks using EwWINNotebook is
simpler than with EwPMNotebook because only few aspects of the widgets
behavior are under application control. There are only two resources; the height of
the tabs, tabHeight, and the number of tabs per row, tabsPerRow. Following is an
example of a WIN notebook widget:

Some resources of an EwPage have no effect in a Windows notebook. Because a


Windows notebook only displays tabs across the top and has no status area, the
tabStyle and pageLabel resources have no effect.

The previous example can be modified to use an EwWINNotebook by substituting


the following notebook creation code:
...
notebook := shell
createWINNotebook: 'notebook'
argBlock: [:w | w
tabsPerRow: 4;
tabHeight: 35].
notebook manageChild.
...

Progress bar widget


Progress bar widgets (EwProgressBar) reflect the current status of an application
task. The status is displayed as a percentage of the task that is complete. A
horizontal or vertical bar is periodically updated by the application to indicate the
progress of a particular task.

Progress is demonstrated visually by filling the progress bar with the widgets
foreground color or by displaying a specified ribbonImage. The application is
responsible for periodically updating the fractionComplete resource to indicate the
extent to which the task has been completed.

Resources are provided which allow the application to control the visual
appearance of the progress bar widget.

Chapter 8. Extended Widgets 253


The orientation of the progress bar is specified using the orientation resource. The
direction resource is used to determine which way the bar is filled in. For example,
a progress bar with a vertical orientation and forward direction will fill in from the
top to the bottom.

The shadowType and shadowWidth resources allow shadows around the bar to be
specified, while the size of the bar itself within the widget can be specified using
the innerHeight and innerWidth resources. The application may also use the
showPercentage and fontList resources to specify a percentage complete label and its
associated font.

The following method creates a progress bar with a thick shadow that fills in from
the bottom to the top. The Motif information icon is used to fill in the bar. The
percentage complete is shown in the bar using the default font. Add this method
to a class that uses the pool dictionaries CwConstants and EwConstants.
progressBarExample
| progressBar shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w
title: 'Progress Bar Example';
width: 210;
height: 100].
progressBar := shell
createProgressBar: 'progressBar'
argBlock: [:w | w
orientation: XmVERTICAL;
direction: XmREVERSE;
ribbonImage: (CgScreen default
getIcon: 'default_xm_information'
foregroundColor: CgRGBColor black);
shadowType: XmSHADOWIN;
shadowWidth: 5;
fractionComplete: 1/4;
showPercentage: true].
progressBar manageChild.
shell realizeWidget.

When you run the method by evaluating <class name> new progressBarExample,
you get the following:

Slider widget
The slider widget (EwSlider) displays an analog representation of a value within a
specified range of values. The range is represented by a horizontal or vertical shaft.
The currentValue resource reflects the position of a slider arm that can be moved
along the length of the shaft to display the current value.

The orientation of the slider can be specified using the orientation resource. A value
of XmVERTICAL indicates that the slider will be drawn vertically, with the
minimum value at the bottom and the maximum value at the top. Similarly,

254 IBM Smalltalk: Programmers Reference


XmHORIZONTAL indicates that the slider will be drawn horizontally, with the
minimum value at the left and the maximum value at the right. Following are
horizontal (on the left) and vertical (on the right) slider widgets:

The slider widget can display one or two scales along the shaft, which can be
displayed above and below a horizontal slider, or to the left and right of a vertical
slider. Both scales provide a minimum value, a maximum value, and a resolution.
The resolution represents the size of the increments between the minimum and
maximum values. For example, using a minimum of 0, a maximum of 100, and a
resolution of 10 would result in valid values of 0, 10, 20...100. Minimum value,
maximum value, and resolution for each scale are set using the topOrLeftScaleMin,
topOrLeftScaleMax, topOrLeftScaleResolution, bottomOrRightScaleMin,
bottomOrRightScaleMax, bottomOrRightScaleResolution resources.

The methods addDetentToScale:at:label: and addTickToScale:at:label: can be used to add


detents and tick marks to a scale. Detents are small images that represent a
particular location on the slider scale. They can be clicked to position the slider
value to the detents location. A tick is a mark that can be used to label a particular
location on the shaft.

Several resources control the visual appearance of the slider. The thickness of the
slider shaft can be controlled using the thickness resource. The buttonStyle resource
provides the option of increment and decrement buttons on the shaft. If requested,
these buttons can be positioned together at either end of the shaft, or separately,
one at each end of the shaft. Further refinements to the shaft position within the
slider widget can be made using the verticalMargin and horizontalMargin resources.

The slider value is changed by moving the slider arm with the mouse, depressing
the increment or decrement buttons, using the arrow keys, or clicking on a detent.
The slider arm moves in units specified by the resolution of the scale. If two scales
are displayed on a slider, the resolution depends on the setting of the activeScale
resource.

A slider can be used as a progress bar by setting the readOnly and ribbonStrip
resources to true and setting the currentValue resource to show the progress.

Slider widgets provide two callbacks to alert applications to changes in the value
of the slider. The dragCallback notifies applications of the movement of the slider
arm. The valueChangedCallback notifies applications when the value has been
changed, that is, after a drag is complete, or if the buttons or detents are used to
change the value.

The following method creates a horizontal slider with tick marks along the top
scale. Add this method to a class using the pool dictionaries CwConstants and
EwConstants.
sliderExample
| slider shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w

Chapter 8. Extended Widgets 255


title: 'Slider Example';
width: 350;
height: 100].
"Create a horizontal slider. The top scale ranges from -100 to 100.
The scrolling buttons appears split (one button on each end of the
slider)."
slider := shell
createSlider: 'slider'
argBlock: [:w | w
orientation: XmHORIZONTAL;
activeScale: XmTOPORLEFT;
topOrLeftScaleMin: -100;
topOrLeftScaleMax: 100;
topOrLeftScaleResolution: 5;
ribbonStrip: true;
buttonStyle: XmBUTTONSSPLIT].
"Add tick marks to the top scale".
-100 to: 100 by: 10 do: [:value |
slider
addTickToScale: XmTOPORLEFT at: value size: 5
label: value printString].

slider manageChild.
shell realizeWidget.

When you run the method by evaluating <class name> new sliderExample, you
get the following:

Spin button widget


A spin button (EwSpinButton) provides an interface for selecting a single value
from a collection of strings or a range of numbers. The widget consists of an entry
field that displays the current value and two arrow buttons that you can use to
scroll forward and backward through the values. You can also scroll through the
values using the up and down arrow keys on the keyboard. The value in the text
entry field can be edited directly if the editable resource is set to true. Following is
an example of a spin button widget:

The itemType resource specifies whether the spin button is to display numeric data
or a collection of strings. Possible values for this resource are as follows:
XmSBNUMERIC
The spin button holds numeric data
XmSBSTRING
The spin button holds a collection of strings

The minimum, maximum, and increment resources can be used to define the range
values for a numeric spin button. These resources are ignored for spin buttons that
hold collections of strings.

256 IBM Smalltalk: Programmers Reference


EwSpinButton also provides several callbacks for application interaction. Callback
protocol is available for gaining and losing focus (focusCallback and
losingFocusCallback), incrementing and decrementing the current value
(incrementCallback and decrementCallback), and activation of the default button
(activateCallback). A less specific callback is provided for any change to the current
value (valueChangedCallback). An additional callback is provided to indicate that the
value in the entry field has been edited by users (modifyVerifyCallback).

The following method creates a spin button that traverses the days of the week.
Add this method to a class using the pool dictionaries CwConstants and
EwConstants.
spinButtonExample
| spinButton shell |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w
title: 'SpinButton Example';
width: 110;
height: 30].
spinButton:= shell
createSpinButton: 'spinButton'
argBlock: [:w | w
itemType: 2; "Or XmSBSTRING instead of 2"
items: #('Monday' 'Tuesday' 'Wednesday' 'Thursday'
'Friday' 'Saturday' 'Sunday');
wrap: true].
spinButton manageChild.
shell realizeWidget

When you run the method by evaluating <class name> new spinButtonExample,
you get the following:

Split window widget


A split window (EwSplitWindow) arranges widgets within a composite. Users can
resize the visible potion of the child widgets in a split window by dragging split
bars. The orientation resource controls the layout of the widgets. A value of
XmHORIZONTAL causes the child widgets to be added from left to right, in the
order they are created; a value of XmVERTICAL causes the child widgets to be
added from top to bottom in the order they are created. Split bars are
automatically inserted between child widgets.

The childProportions resource can specify the initial size of the child widgets. The
childProportions resource contains an array of integers indicating the relative
percentage of the window to be occupied by each child. If the resource is not
specified, the child widgets are sized equally within the window.

Users can reposition split bars by first pressing and holding mouse button one
while the cursor is over a split bar and then moving the mouse to the desired
position. Dragging one split bar towards another causes the visible portion of the
enclosed widget to be reduced until it reaches a predefined minimum size as
defined by the childMinimumSizes resource.

Chapter 8. Extended Widgets 257


You can change the visual appearance of split bars using the
splitBarForegroundColor, splitBarBackgroundColor, and splitBarThickness resources.

The following method creates a split window with three child widgets. Child
widgets are given initial proportions and minimum sizes. Add this method to a
class using the pool dictionaries CwConstants and EwConstants.
splitWindowExample
| shell splitWindow |

shell := CwTopLevelShell
createApplicationShell: 'Split Window Example'
argBlock: nil.
splitWindow := EwSplitWindow
createManagedWidget: 'splitWindow'
parent: shell
argBlock: [:w | w
width: 400;
height: 200;
orientation: XmHORIZONTAL;
childProportions: #(60 15 25) ;
childMinimumSizes: #(30 15 15)].
CwList
createManagedWidget: 'list1'
parent: splitWindow
argBlock: [:w | w
borderWidth: 0;
items: (Collection withAllSubclasses collect: [:each | each name])].
CwList
createManagedWidget: 'list2'
parent: splitWindow
argBlock: [:w | w
borderWidth: 0;
items: (Collection class selectors collect: [:each | each printString])].
CwList
createManagedWidget: 'list3'
parent: splitWindow
argBlock: [:w | w
borderWidth: 0;
items: (Collection selectors collect: [:each | each printString])].
shell realizeWidget.

When you run the method by evaluating <class name> new splitWindowExample,
you get the following:

258 IBM Smalltalk: Programmers Reference


Tool bar widget
A tool bar (EwToolbar) provides an interface for building a horizontal or vertical bar
containing user interface tools such as push buttons and labels. You can use tool
bar widgets to implement the tool bars commonly found under the menu bar in
GUI applications. You can use them to provide rows or columns of tools anywhere
else in a window. Further, you can use them to implement status bars that allow
applications to display various kinds of messages.

Several resources can control the way that a tool bar displays its tools. The
numColumns resource can specify whether the tools should be arranged in columns.
If the numColumns is 0, the tool bar lays out the tools in a row. Setting numColumns
to 1 produces a vertical tool bar. The spacing resource determines how much space
to leave between each tool.

You can specify the colors of the tool bar using the standard foregroundColor and
backgroundColor resources. In addition, you can specify the default colors of tools
using the toolForegroundColor and toolBackgroundColor resources.

Creating tools
The tool bar usually does not have widgets as children. Instead, it has specialized
children called tools (EwTool). Tools are user interface elements that look like
widgets but do not actually use all of the platform resources required by a widget.
Tools collaborate with the parent tool bar to display themselves and handle user
events. You add widgets as children of a tool bar by associating a widget with a
widget tool.

Tools are created by sending one of the following messages to a tool bar:
createGroup:argBlock:
Creates a group tool inside the tool bar. The first argument is the name for
the new tool; the second is its argument block. A group is a tool that
contains other tools.
createLabelTool:argBlock:
Creates a label tool inside the tool bar.
createProgressBarTool:argBlock:
Creates a progress bar tool inside the tool bar.
createPushButtonTool:argBlock:
Creates a push button tool inside the tool bar.
createRadioButtonTool:argBlock:
Creates a radio button tool inside the tool bar.
createSeparatorTool:argBlock:
Creates a separator tool inside the tool bar.
createSimpleGroup:argBlock:
Creates a simple group inside the tool bar. A simple group is a tool that
contains only buttons. It provides protocol for specifying collections of
images to be displayed in the group as buttons.
createToggleButtonTool:argBlock:
Creates a toggle button tool inside the tool bar.

Chapter 8. Extended Widgets 259


createWidgetTool:name:argBlock:
Creates a widget and adds it as a tool to the tool bar. The first argument is
the class of widget to be created; the second is its name; the third is its
argument block.

Tools are lighter weight than widgets and minimize platform resources. However,
they do not always look like a platform widget. Using widgets and widget tools on
the tool bar ensures that the tools look exactly like their widget counterparts on a
particular platform. This platform look and feel, though, comes at the expense of
using platform resources.

Using tools
Tools on a tool bar can be used much like widgets. Resources such as width, height,
borderWidth, foregroundColor, and backgroundColor can control the appearance of the
tool. The variableWidth resource controls whether a tool shrinks or grows as the tool
bar resizes.

The enterNotifyCallback and leaveNotifyCallback can determine when the mouse


enters or leaves the location on the tool bar occupied by a tool. These callbacks are
useful for implementing features such as bubble help or tool tips or for updating a
status area.

Using primitive tools


The most commonly used tools on a tool bar are primitive tools. These are tools
that behave much like simple widgets such as buttons and labels. Primitive tools
have an image which determines the text or graphics that they display. The image
can be any renderable object (see Renderables on page 238).

Label tools (EwLabelTool) display an image and provide a shadowed inset as


specified in the shadowType resource. They provide resources for setting margins
similar to the CwLabel widget. Progress bar tools (EwProgressBarTool) look and
behave much like EwProgressBar. The button tools (EwPushButtonTool and
EwToggleButtonTool) also behave much like their CwWidget counterparts. A
separator tool (EwSeparatorTool) can separate tools or clusters of tools. The
separatorType resource determines the exact appearance of a separator tool.

The following method creates a tool bar with four primitive tools. Add this
method to a class using the pool dictionaries CwConstants and EwConstants.
toolBarExample
| shell toolBar |
shell := CwTopLevelShell
createApplicationShell: 'Tool Bar Example'
argBlock: nil.

toolBar := EwToolBar
createManagedWidget: 'toolBar'
parent: shell
argBlock: [:w | w numColumns: 0; spacing: 3].
toolBar
createLabelTool: 'label'
argBlock: [:w | w image: 'Text Label'].
toolBar
createSeparatorTool: 'label'
argBlock: [:w | w
orientation: XmVERTICAL;
autoSize: true;
separatorType: XmSHADOWETCHEDIN].

260 IBM Smalltalk: Programmers Reference


toolBar
createProgressBarTool: 'progressBar'
argBlock: [:w | w fractionComplete: 1/2].
toolBar
createPushButtonTool: 'pushButton'
argBlock: [:w | w image: 'Push Me'; width: 80].

shell realizeWidget.

When you run the method by evaluating <class name> new toolBarExample, you
get the following:

Using widget tools


Widgets can be added to a tool bar by creating a widget tool. A widget tool can be
created by sending the createWidgetTool:name:argBlock: message to the toolbar. When
adding a widget tool, the application specifies the class of widget to be created.

The argument for a widget tool argBlock is the tool itself, not the widget. To set
resources for the tools associated widget, use the widget resource to access the
widget from the tool.

The following example creates a widget tool using a combo box. It uses the
argument block to set both widget and tool resources.
...
toolBar createWidgetTool: CwComboBox name: 'combo' argBlock: [:tool |
tool
borderWidth: 1;
height: 36.
tool widget
items: #('Red' 'Green' 'Blue');
editable: true].
...

Using groups
Groups (EwGroupTool) are useful for grouping multiple tools in order to assign
common properties or provide specialized behavior for the group. The shadowType
resource determines what kind of border is drawn around the tools within a group
when the borderWidth is not 0. The spacing resource determines what spacing the
group uses for its tools. If the group contains toggle button tools, the radioBehavior
resource can enforce radio button style behavior within a group.

A group is created like any other toolby sending either createGroup:argBlock: or


createSimpleGroup:argBlock: to the toolbar. Tools are added to the group by sending
tool creation messages to the group instead of the tool bar.

The following method below creates a tool bar that contains two groups separated
by a separator tool.
toolGroupExample
| shell toolBar group1 group2 |
shell := CwTopLevelShell
createApplicationShell: 'Tool Group Example'
argBlock: nil.

toolBar := EwToolBar

Chapter 8. Extended Widgets 261


createManagedWidget: 'toolBar'
parent: shell
argBlock: [:w | w numColumns: 0; spacing: 3].
group1 := toolBar
createGroup: 'group1'
argBlock: [:w | w borderWidth: 1; shadowType: XmSHADOWIN].
group1
createLabelTool: 'label'
argBlock: [:w | w image: 'Text Label'].
group1
createProgressBarTool: 'progressBar'
argBlock: [:w | w fractionComplete: 1/2].

toolBar
createSeparatorTool: 'label'
argBlock: [:w | w
orientation: XmVERTICAL;
autoSize: true;
separatorType: XmSHADOWETCHEDIN].
group2 := toolBar
createGroup: 'group2'
argBlock: [:w | w
borderWidth: 1;
shadowType: XmSHADOWOUT;
radioBehavior: true].
group2
createToggleButtonTool: 'toggle1'
argBlock: [:w | w image: 'Yes'; radioBehavior: true].
group2
createToggleButtonTool: 'toggle2'
argBlock: [:w | w image: 'No'; radioBehavior: true; set: true].

shell realizeWidget.

Evaluating <class name> new toolGroupExample displays the following:

Simple groups (EwSimpleGroupTool) provide convenience methods for creating


groups of buttons by simply specifying a collection of images from which button
tools will be created. The buttonBorderWidth, radioBehavior, and toggleBehavior
resources specify the appearance and behavior of the buttons.

The example below creates a group like the radio group in the previous example,
but it uses a simple group to create the toggle buttons.
...
group2 := toolBar createSimpleGroup: 'group2' argBlock: [:w | w
borderWidth: 1;
shadowType: XmSHADOWOUT;
radioBehavior: true;
toggleBehavior: true;
images: #('Yes' 'No')].
...

262 IBM Smalltalk: Programmers Reference


Chapter 9. Drag and Drop
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.
No platform-specific drag and drop API is used. Platform-integrated drag and
drop on page 215 covers drag and drop among applications. This chapter covers
drag and drop within a Smalltalk application. Currently, drag and drop is limited
to widgets in a single Smalltalk image.

An application might wish to support drag and drop for a variety of reasons. It is
not the purpose of Drag and Drop subsystem support to prescribe the semantics of
drag and drop; this is left to the application. Instead, generalized objects called
adapters are provided to map low-level mouse and keyboard operations to drag
and drop callbacks. The primary role of the application in drag and drop is to
create the widgets and adapters required, hook the drag and drop callbacks on the
adapters, and then make the changes to the underlying objects as drag and drop
occurs.

Because the adaptors hook low-level mouse and keyboard events, any widget that
provides these events can be adapted to drop and drag. This includes those
widgets in the Common Widgets subsystem that fully support mouse and
keyboard events.

Drag and drop adapters


The hierarchy of drag and drop adapter classes is the following:

Class hierarchy Responsibility


EwDragAndDropAdapter Defines common behavior for all adapters
EwSourceAdapter Defines common behavior for all drag and drop
sources
EwBlockSourceAdapter Defines a drag and drop source that uses an
application-defined block to determine the drag
source items
EwContainerListSourceAdapter Defines a source adapter for EW container- style
widgets
EwIconAreaSourceAdapter Defines a source adapter for EwIconArea widgets
EwDrawnListSourceAdapter Defines a source adapter for EwDrawnList widgets
EwTargetAdapter Defines common behavior for all drag and drop
targets
EwContainerListTargetAdapter Defines a target adapter for EW container-style
widgets
EwIconAreaTargetAdapter Defines a target adapter for EwIconArea widgets

The Drag and Drop subsystem provides two kinds of adapters for use in drag and
drop: source adapters and target adapters. The adapters serve as a wrapper around
widgets, providing drag and drop callbacks that the widgets themselves do not
provide.

Copyright IBM Corp. 1994, 2000 263


An application should create a source adapter for each widget that an application
designates as a drag and drop source.

An application creates one source adapter for each source widget as follows:
aSourceAdapter := EwSourceAdapter on: aWidget.

The source adapter provides the following callbacks for drag and drop source
operations:
dragStartCallback
A drag has been started from the source widget.
dragChangeCallback
The target or semantics of the drag have changed.
dragCompleteCallback
The drag and drop operation has completed.
dragCancelCallback
The drag and drop operation was canceled or did not complete.

The application also should create a target adapter for each widget that is to be a
target for drag and drop. The target adapter is created as follows:
aTargetAdapter := EwTargetAdapter on: aWidget

The target adapter provides the following callbacks for drag and drop target
operations:
dragOverCallback
The mouse pointer is moving over the drag target widget.
dragLeaveCallback
The mouse pointer has left the drag target widget.
dropCallback
The drop operation was indicated over the drag target widget.
dragCancelCallback
The drag and drop operation was canceled or did not complete.

Sequence of events
An application prepares for drag and drop by creating a source adapter on the
widgets that acts as the drag source. It then hooks each source adapters
dragStartCallback and dragCompleteCallback and, optionally, the dragChangeCallback
and dragCancelCallback.

An application also creates a target adapter on each widget that can act as a drop
target, including any source widgets that can also act as targets. An application
then hooks each target adapters dragOverCallback and dropCallback and, optionally,
the dragLeaveCallback and dragCancelCallback.

In the following example, a window is created with a single EwIconList on it. This
list is configured to do drag and drop to and from another such window. This
portion of the example creates the widgets and adapters, hooks the necessary
callbacks, and realizes the window. The visualInfoCallback handler is also shown for
completeness. The application containing the class should have EwDragAndDrop for
a prerequisite.

264 IBM Smalltalk: Programmers Reference


Object subclass: #DragAndDropExample
instanceVariableNames: 'icon'
classVariableNames: "
poolDictionaries: 'CwConstants EwConstants '
openOn: aCollectionOfObjects
"Open the example, showing a list of objects."
| shell iconList sourceAdapter targetAdapter |
shell := CwTopLevelShell
createApplicationShell: 'shell'
argBlock: [:w | w
title: 'Drag and Drop Example';
width: 250;
height: 300].
iconList := shell
createScrolledIconList: 'iconList'
argBlock: [:w | w
selectionPolicy: XmEXTENDEDSELECT;
items: aCollectionOfObjects asOrderedCollection].
iconList manageChild.
iconList
addCallback: XmNvisualInfoCallback
receiver: self
selector: #visualInfo:clientData:callData:
clientData: nil.
sourceAdapter := EwSourceAdapter on: iconList.
sourceAdapter
addCallback: XmNdragStartCallback
receiver: self
selector: #dragStart:clientData:callData:
clientData: nil;
addCallback: XmNdragCompleteCallback
receiver: self
selector: #dragComplete:clientData:callData:
clientData: nil.
targetAdapter := EwTargetAdapter on: iconList.
targetAdapter
addCallback: XmNdragOverCallback
receiver: self
selector: #dragOver:clientData:callData:
clientData: nil;
addCallback: XmNdropCallback
receiver: self
selector: #drop:clientData:callData:
clientData: nil.
"Note: The destroyCallback is not shown."
icon := shell screen
getIcon: 'default_xm_information' foregroundColor: CgRGBColor black.
shell realizeWidget.
visualInfo: widget clientData: clientData callData: callData
"Provide the icon and label for the object contained in callData."
callData
icon: icon;
label: callData item.

The user initiates a drag by pressing the drag mouse button (ButtonDrag) over a
drag source widget and moving the mouse a certain nominal distance. (The
ButtonDrag is button 1 on Windows and UNIX platfoms, and button 2 on OS/2.
Note that these buttons are settable.) The source adapter, having hooked the mouse
events on the source widget, detects that a drag is starting and activates the
dragStartCallback to the application. The callData for the dragStartCallback includes
the mouse event that triggered the drag start, the items in the source widget that
are being dragged, and each items offset from the mouse location. It also contains

Chapter 9. Drag and Drop 265


a default image for each item to be used to represent the item during the drag. The
images and offsets can be modified by the application to cause alternate images
and offsets to be used. For example, an application might wish to use a multi-file
icon to represent all items being dragged rather than a single icon for each item.

The images and offsets that the adapter provides as defaults depend on the API of
the source widget. For example, because CwList provides no API to allow the
source adapter to determine which item in the list is under the cursor when the
drag starts, the adapter simply provides the selected items as the drag items.
Similarly, because the source adapter cannot determine the offsets of the selected
items, it provides offsets that cause the drag items to be beveled up and to the
right from the mouse.

The dragStartCallback callData also contains a doit flag that is set to true by default.
The application can change this flag to false if it determines that dragging is not
allowed for some reason. The callData also includes a default vote (see Voting
and cursors on page 267), which is an array of operations that the source allows
for these items. The application can change this vote to an array of operations that
it will allow, for the source items being dragged.

Finally, the dragStartCallback callData contains a slot for the source model. The
application can place any value in this slot, but typically it is used to hold the
underlying object that contains the items being dragged. This value will be passed
along to the drag target in the callData of the target-related callbacks.

This portion of the example shows a sample dragStartCallback handler:


dragStart: sourceAdapter clientData: clientData callData: callData
"The items in the callData are being dragged. Allow only
move operations, not copy or link."
callData
doit: true;
vote: (Array with: XmMOVE)

Each time the mouse moves, the system determines which widget is currently
under the mouse. The system keeps a registry of all target adapters and their
widgets. This enables it to map the widget under the mouse (if any) to its
corresponding target adapter (if any).

If a widget is not under the mouse or if a target adapter does not exist for a
widget, the cursor which indicates that the operation is not allowed (that is, the
No Parking cursor) is automatically shown.

If a target adapter is found, the dragOverCallback is sent to its application. The


dragOverCallback callData contains the items being dragged, the source widget, and
the mouse event. The target adapter also determines which item in the target
widget is under the mouse and supplies this item in the callData. This is only
possible on widgets that provide the necessary API to determine this information.
Because CW widgets provide no API to support this, the item under the cursor is
always nil for target adapters on base widgets.

If there is an API to determine which item is under the cursor (as is the case in
some extended widgets), the application must determine whether the item under
the cursor is itself capable of accepting a drop. For example, a trash can or a
printer typically would be able to accept a drop.

The callData also contains an emphasis flag that an application can set to specify
which kind of emphasis should be shown on the target widget. Again, this is not

266 IBM Smalltalk: Programmers Reference


possible on any of the CW widgets; only EwIconList, EwIconTree, EwFlowedIconList,
EwTableList and EwTableTree use the emphasis flag. If an application has determined
that the item under the cursor is capable of receiving the drop, it sets the emphasis
flag to XmTARGETEMPHASIS. If it determines that the item is not capable of
receiving the drop, then it can set the emphasis flag to either
XmINSERTIONEMPHASIS or XmNOEMPHASIS (default).
XmINSERTIONEMPHASIS indicates that if the drag items are dropped, they will
be inserted into the target widget at the index determined by the mouse position.

The dragOverCallback callData also contains the source model and a field for the
target model. The source model is whatever value was last placed in the
sourceModel slot by the source in either the dragStartCallback or dragChangeCallback
callData. The application can set the target model to be any object, but this is
typically the object that would contain the drag items if they were to be dropped
on the target.

The dragOverCallback callData also contains a vote. The application can set this to
be an array of operations that it will allow given the current target widget, target
item (if any), and the items being dragged.

Finally, the dragOverCallback callData contains a repeat flag. This Boolean variable
determines whether another dragOverCallback should be activated even if the item
under the cursor does not change. By default, this value is false for performance
reasons. If the application needs the dragOverCallback to be triggered continuously,
then the repeat field must be set to true each time the callback is triggered. For
example, if an application uses a drawing area to present an image, the application
might need the dragOverCallback to be triggered repeatedly so that it can detect
when the items are being dragged over the various parts of the image.

This portion of the example shows a sample dragOverCallback handler:


dragOver: targetAdapter clientData: clientData callData: calldata
"The items in the calldata are being dragged over the receiver.
These items might be from another source. Allow only move
operations between different lists, not copy or link, and not
within the same list."
calldata sourceWidget == targetAdapter widget
ifTrue: [calldata vote: #()]
ifFalse: [
calldata
vote: (Array with: XmMOVE);
emphasis: XmINSERTIONEMPHASIS]

Tip: In some cases, an application might need to draw while a drag is in progress
(for example, to change the icon of an item under the cursor). To avoid
leaving visual debris on the screen, the application must do any drawing
by sending the message drawDuringDrag: to the adapter. This message takes a
Block as its argument. All drawing that takes place in the block will not leave
debris on the screen.

Voting and cursors


As items are being dragged, users can affect the drag operation by pressing the
shift key, the control key, or both keys. Each time a dragOverCallback is activated, or
each time the keyboard status changes, the source, target, and keyboard votes are
recalculated, and a net operation is determined. This operation, in turn, determines
what the cursor should be.

Chapter 9. Drag and Drop 267


Because dragging images causes the cursor to blink, an alternative to cursors,
called cursor images, is provided. Like cursors, each cursor image corresponds to
an operation. The difference is that if a cursor image is defined for an operation, it
is drawn over all the drag images and will not blink as the mouse is moved. By
default, the system turns off the cursor during drag and drop and uses a unique
cursor image for each operation.

The cursor images and keyboard mappings for each operation are configurable on
a global basis (see System configuration on page 269).

Source vote and image changes


Some applications might require that the source be able to change its vote, its drag
images, or its drag offsets based on the target, the operation, or both. For example,
the source might want to allow certain operations only if the item is being dragged
over the source. In this case, the source would need an opportunity to change its
vote whenever the target changed.

To support this requirement, the dragChangeCallback on the source adapter is


activated whenever the target or the operation changes. The callData includes the
items being dragged, the target widget, target item, target model, and the current
operation. It also includes the drag images and offsets and the most recent source
vote and source model. The application can change these values to implement
behaviors like those outlined in the example above.

Leaving a target
In some cases, an application might need to be notified when items are dragged
away from a target. For example, if an application had hooked the dragOverCallback
to show target emphasis, it would need to be notified when the drag had left that
target widget so that it could erase the emphasis.

To support this, the dragLeaveCallback on the target adapter is activated whenever


the items are dragged away from the target of the last dragOverCallback. The
callData includes only the source widget, the source model, and the source items.
None of these fields can be changed; this callback is for notification only.

Dropping
When a user releases the mouse button, a drop occurs on the target widget. Both
the target and the source are notified. The target adapter first activates the
dropCallback. The callData includes the source widget, the source model, the source
items, the operation, and the mouse event. It also includes an offset for each source
item relative to the mouse location. If, in response to the last dragOverCallback, the
application set the emphasis to XmTARGETEMPHASIS, the callData for the
dropCallback also contains the target item. If the emphasis was set to
XmINSERTIONEMPHASIS, the callData contains the insertion index. Finally, the
callData contains a doit flag, which an application can set to false if it is unable to
perform the drop. In this case, the source adapter will activate its
dragCancelCallback. Otherwise, it is the applications responsibility to perform the
appropriate operation on the target widget and the underlying objects.

This portion of the example shows a sample dropCallback handler:


drop: targetAdapter clientData: clientData callData: calldata
"The items in the callData are being dropped onto the
receiver. These items must be from another source."
| toAdd |

268 IBM Smalltalk: Programmers Reference


toAdd := calldata sourceItems reject: [:anObject |
targetAdapter widget items includes: anObject].
targetAdapter widget addItems: toAdd position: calldata insertionIndex

After the target has triggered the dropCallback, the source adapter activates its
dragCompleteCallback. The callData includes the source items, the target widget, the
target model, the target item (if any), and the operation. It is the applications
responsibility to perform the appropriate operation on the source widget and the
underlying objects. For example, if the operation was XmMOVE, the application
should remove the items from the source widget.

This portion of the example shows a sample dragCompleteCallback handler:


dragComplete: sourceAdapter clientData: clientData callData: calldata
"The items in the calldata have been dropped somewhere else."
calldata vote == XmMOVE ifTrue: [
sourceAdapter widget deleteItems: calldata sourceItems]

Canceling a drag
The user can cancel a drag at any time by pressing the key represented by
XkCancel. In this case, the source adapter and the target adapter (if any) activate
their dragCancelCallback. The callData for this callback depends on whether the
adapter is a source or a target adapter. For source adapters, the callData includes
the same information as the callData for the dragCompleteCallback. For target
adapters, the callData includes the same information as the callData for the
dropCallback.

The source adapter also activates the dragCancelCallback when items are dropped
outside any target widgets. The dragCancelCallback is also triggered when the items
are dropped onto a target that is unwilling to accept them; that is, when no valid
operation is in effect or when the target widgets application sets the doit flag to
false in the dropCallbacks callData.

System configuration
A central drag and drop manager (EwDragAndDropManager) stores system-wide
drag and drop parameters. It provides API to set and query: the list of possible
operations, the cursors that correspond to each operation, and the mappings from
keyboard combinations to operations.

The mouse button that should be used to initiate a drag operation is specified in
the button resource. An application can specify a particular button using Button1,
Button2, or Button3, or it can use the value ButtonDrag to indicate that the default
drag button for a particular platform should be used.

The set of operations and their relative priorities are configurable. By default, the
operations are XmMOVE, XmCOPY, and XmLINK (a Link is a reference to the
same object). When the source, target, and keyboard votes are tallied, the highest
priority operation of the intersection of the three votes is used as the operation.
The priority of the votes is determined by the order the source answers its
allowable operations in the dragStartCallback. The highest priority vote is the first
vote the source gives. If the intersection is empty, then the cursor which indicates
that an operation is not allowed is shown.

The mapping of the shift and control key combinations to drag operations can be
customized, too. The default mappings are:
None All operations are allowable.

Chapter 9. Drag and Drop 269


Shift XmMOVE
Control
XmCOPY
Shift + Control
XmLINK

The cursor images to be shown for an operation are also configurable. The default
mappings are:

Note: The button can be set to start drag.

Simple drag and drop


For most applications, the default settings are acceptable. This section describes the
minimum requirements for an application to enable drag and drop.

At a minimum, the application must create the source and target adapters on the
appropriate widgets. On the source adapter it does not need to hook the
dragStartCallback unless it intends to change the vote, images, or offsets to values
other than the defaults or unless it wants to deny drag and drop in certain cases
by setting the doit flag to false. An application must hook the dragCompleteCallback
to perform the operation. The dragChangeCallback does not need to be hooked, nor
does the dragCancelCallback.

On the target adapters, the application only needs to hook the dragOverCallback if it
needs to check the kind of items being dragged to ensure that the target widget
can receive them or if it does not want the default target vote. It should hook the
dropCallback to perform the operation. It does not need to hook the
dragLeaveCallback or dragCancelCallback.

Tip: Most of the extended widgets are useful without drag and drop, but for
EwIconArea widgets, drag and drop is needed just to move the items around
within the widget. The EwIconArea widget provides an API to support
internal-only drag and drop without having to create a source and target
adapter, and without having to hook any callbacks. The method
configureForDefaultDragAndDrop automatically does this. It limits drag and
drop to only allow the user to move items within the container. If the
application requires any additional capabilities, it should not use this simple
API, but should create its own adapters and hook the callback accordingly.

Widget limitations
Technically, any widget is capable of drag and drop, although some types of
widgets are better suited than others. Drag and drop works best on container style
extended widgets such as EwIconArea, EwIconList, EwFlowedIconList, EwIconTree,
EwTableList, and EwTableTree. On these widgets, the sourceItems, targetItem, and

270 IBM Smalltalk: Programmers Reference


itemUnderCursor fields in the various callData always contain actual items from the
source and target container. Furthermore, these container widgets assume that if
dragging starts on one of the selected items, then all of the selected items are
dragged. They also assume that if dragging starts on a non-selected item, then only
that item is dragged.

Drag and drop for the other list-oriented widgets such as CwList and CwComboBox
carries some limitations. First, the sourceItems, targetItem, and itemUnderCursor fields
in the various callData only ever contain strings when the callbacks are triggered.
However, the application can override these values in the callback handlers. Also,
because these widgets do not provide any way to determine which items are under
the cursor, they always assume that, regardless of where a drag actually starts, all
selected items are dragged. Drag and drop on these widgets is further limited by
the fact that they cannot show insertion or target emphasis. This implies that it is
not possible to inform users where items will be inserted if they are dropped on
one of these widgets.

Drag and drop to and from non-list-oriented widgets is more severely limited.
Because there are no commonly accepted semantics for drag and drop on labels,
buttons, forms, or other non-list-oriented widgets, the application must define
what is meant, for example, by dragging from a CwLabel to a CwToggleButton. Also,
the sourceItems, targetItem, and itemUnderCursor fields in the various callData are
always left nil.

Chapter 9. Drag and Drop 271


272 IBM Smalltalk: Programmers Reference
Chapter 10. Common Printing
The Common Printing (CP) subsystem provides an event-driven printing model
with support for job and page control, job setup, and printer font selection. Text
and graphics, including bitmapped images, can be printed using the Common
Graphics API.

Common Printing classes


This section describes printing-related terminology and the classes defined by
Common Printing.

A print device, referred to in this text as a printer, is any device capable of


producing printed output. An instance of the CgPrinterScreen class represents a
single printer.

A print job is a document stored within the computer that contains zero or more
pages of output. Print jobs have platform- and device-specific attributes associated
with them such as print resolution, form selection, special printer effects such as
orientation and duplex printing, and so on. Common Printing encapsulates these
attributes in the CgPrintJobAttributes class.

A CwPrinterShell provides job and page control methods, and processes printing
events. The window associated with a CwPrinterShell represents the printable area
on the physical page. Applications use Common Graphics operations to draw on
this window.

Users can choose from available printers using a CwPrinterPrompter. Printer


prompters also allow users to configure print job attributes.

A print server is an object that manages one or more printers and common
resources such as printer fonts. In IBM Smalltalk, a connection to a print server is
represented by a CgDisplay. Each CgDisplay contains at least one CgPrinterScreen
object and zero or more CgScreen objects.

The following diagram shows how the classes are related from the applications
point of view.

Copyright IBM Corp. 1994, 2000 273


Printing process overview
This overview describes printer selection, print job attribute configuration, and
print job configuration.

Selecting a printer
There are several different approaches to selecting a printer. The recommended
approach is to allow the user to choose a printer display using a printer prompter.
For more information on CwPrinterPrompter, see Using the printer prompter on
page 275.

If a user selection is not required, the application can choose to use the default
printer. The default printer can be obtained by sending default to CgPrinterScreen
class. It can be changed via printer configuration tools provided by the operating
system, for example, the Print Manager in Windows or a Printer object in OS/2. If
there is no default printer, default answers nil.

A printer can also be selected by enumerating the printer screens available on a


given display using the printerScreens method. For instance, printer screens on the
default printer display can be obtained using CgDisplay defaultPrinterDisplay
printerScreens. Printers on other displays can be listed by first opening a connection
to the desired display, using CwAppContext openDisplay:, and then sending

274 IBM Smalltalk: Programmers Reference


printerScreens to this printer display. Printer displays opened in this manner should
always be closed when they are no longer needed by issuing close. The default
printer display should not be closed.

Display names used for opening a printer display with openDisplay: can be
obtained from CgDisplay allPrinterDisplayNames. Do not cache the results of
allPrinterDisplayNames because printers can be added or removed from the
operating system at any time. To obtain a human-readable name of a printer, use
CgPrinterScreen name, rather than CgDisplay displayString

Configuring print job attributes


Print job attributes, such as print resolution, form selection, and special printer
effects, are both printer- and platform-specific. A CwPrinterPrompter allows users to
configure these attributes. The CwPrinterPrompter contains a CgPrintJobAttributes
object that describes the printer-specific setup of the printer that the user selected.
For more information on CgPrintJobAttributes, see Print job attributes on page
276.

Creating a print job


To produce documents, the application creates a CwPrinterShell on the desired
printer screen and display. The shell provides methods to start and end print jobs
and to identify page boundaries. Text and graphics are drawn on the window
associated with the printer shell.

The CwPrinterShell notifies the application of printing events using the widget
callback mechanism. The callback model for printing is described in Adding
callbacks on page 277. See the Common Widgets chapter for more information
on callbacks.

Using the printer prompter


The CwPrinterPrompter allows users to select a printer. The user can configure job
attributes for a printer by using a platform- and printer-specific setup dialog. Print
job attributes for the selected printer can be retrieved by sending jobAttributes to
the CwPrinterPrompter after a printer has been selected. A CwPrinterPrompter from
the OS/2 platform is shown in the following diagram.

A printer prompter returns the display name of the selected printer, or nil if no
printer is selected. The prompter also returns nil if no printers are available.

The display name returned by the prompter is used to open a connection to the
desired printer display by sending openDisplay:; to CwAppContext default. A printer
shell can then be created by sending appCreateShell:applicationClass:display:argBlock:;
to the CwPrinterShell class.

Chapter 10. Common Printing 275


When a printer prompter returns successfully (the value is not nil), its jobAttributes
method answers that with a CgPrintJobAttributes compatible with the selected
printer. The job attributes should be used when creating a printer shell by
specifying the jobAttributes: resource message in the create argBlock for the shell.

The following sample code shows typical printer prompter use, including opening
the selected display, retrieving selected job attributes, and creating a printer shell.
| prompter displayName display jobAttributes printerShell |
prompter := CwPrinterPrompter new.

(displayName := prompter prompt) isNil


ifTrue: [|System message: 'A printer was not selected.'].

(display := CwAppContext default openDisplay: displayName) isNil


ifTrue: [|self error: 'Cannot connect to selected printer.'].

jobAttributes := prompter jobAttributes.

printerShell := CwPrinterShell
appCreateShell: 'print job'
applicationClass: nil
display: display
argBlock: [ :w | w jobAttributes: jobAttributes].

Print job attributes


Most printers allow configuration of features such as print resolution, form
selection, simplex or duplex printing, and so on. Common Printing encapsulates
these printing attributes in the CgPrintJobAttributes class.

Print job attributes can be configured by the user in two ways: default attributes
via printer configuration tools provided by the operating system, and job-specific
attributes via the platform- and printer-specific setup dialog of a printer prompter.
The application can obtain print job attributes by sending defaultJobAttributes to a
CgPrinterScreen or by sending jobAttributes to a CwPrinterPrompter after the user has
selected a printer. Attributes can also be saved between sessions.
CgPrintJobAttributes can be passed to a CwPrinterShell in the create argBlock for the
shell using the jobAttributes:; resource setting method.

Print job attributes are platform- and printer-specific, so a CgPrintJobAttributes


object that works on one printer will not necessarily work on another printer, and
will not work on a different platform. CgPrintJobAttributes objects know their
platformName, deviceName, and driverVersion, and they can be asked whether they
are compatible with a certain printer screen using isCompatibleWith:.

Using a printer shell


As mentioned above, a CwPrinterShell is used to create print jobs. The window
associated with a CwPrinterShell corresponds to the physical page on the printer,
and can be drawn on using standard Common Graphics commands. The shell
itself handles job and page control.

CwPrinterShell has the same application program interface (API) as a


CwTopLevelShell widget, with the following exceptions:
v Child widgets are not supported. Graphics can be drawn on the window
associated with the printer shell, but child widgets cannot be added to the shell.
v Video-specific features such as shell iconification are not supported.

276 IBM Smalltalk: Programmers Reference


v Additional printer-specific methods such as resolution are included in the
CwPrinterShell API.

Creating a shell
There are two different methods of creating a printer shell.
v CwPrinterShell>>createApplicationShell:argBlock: creates a printer shell on the
default printer display. Resource-setting messages can optionally be sent to the
shell in the create:argBlock:.
v CwPrinterShell>>appCreateShell:applicationClass:display:argBlock: allows an
application to specify the printer display (the print server) the shell is to be
associated with. This method is typically used when a printer display has been
selected using a printer prompter.

The screen: method can be used in the shells create:argBlock: to specify the
CgPrinterScreen (the print device) the shell is to be associated with. The screen
must be one of the printer screens of the shells display. If a screen is not specified,
the shell simply uses the default printer screen of the display. Because there is
rarely more than one CgPrinterScreen associated with a printer display, the default
printer screen is typically used.

Print job attributes for the shell can also be specified in the create:argBlock: via the
jobAttributes: method. All print jobs produced by the shell take on the given job
attributes. If job attributes are not specified at shell creation time, the shell obtains
default job attributes from its printer screen. Job attributes are not checked for
compatibility with the selected printer, but the application can use
isCompatibleWith:; to test compatibility if desired.

Both printer shell creation methods answer the newly created shell widget, or nil if
the widget cannot be created. An error message is displayed if a printer display or
printer screen is requested that is not valid.

Adding callbacks
Common Printing uses the callback mechanism to inform applications of events
related to the printing process. Callbacks are added to a shell using the
addCallback:receiver:selector:clientData: method.

CwPrinterShell uses the following callbacks:


mapCallback
The shell has been mapped and is ready for print job production.
exposeCallback
The shell is ready to receive data for the current printer page.
resizeCallback
The shells dimensions have been modified.
destroyCallback
The shell is about to be destroyed.

Chapter 10. Common Printing 277


The following diagram shows the proper sequencing of printer shell job commands
and callbacks.

Starting a job
After creation, a printer shell widget must be realized by invoking realizeWidget.
The shell responds with a map callback to indicate readiness for job control
messages. At this point the shell can be used to start print jobs.

Because printing is implemented as a set of user-interface operations on a printer


display, the application must run the user-interface event loop to allow callbacks to
be sent to the printer shell. The application can either fall back to the event loop
after realizing the shell, or it can run a new event loop in place until printing is
complete. Printing operations cannot be performed by processes other than the
user-interface process. For more information see The user interface process
model on page 225.

After reception of a map callback the application sends startJob to the shell, which
begins a new document with the title specified by the shells title resource. The
title is used to identify the document within the operating system, and typically
includes the name of the application producing the document. Title changes only
take effect prior to the beginning of a new print job.

Producing a page
The actual contents of a print job consist of graphics and text created with the
Common Graphics API. See Printing with Common Graphics on page 279 for
more information on printing graphics.

An application uses the following steps to produce pages of graphics and text:
1. Send a startPage message.
2. Upon reception of an expose callback, draw graphics on the window associated
with the shell (window). The number of the page currently being drawn is
specified in the callback data of the expose callback.
3. Send an endPage message.

278 IBM Smalltalk: Programmers Reference


4. Repeat steps 1 to 3 as necessary.

Ending a job
The endJob method ends a print job and submits the job for printing. A job can also
be canceled at any point by calling cancelJob. In this case, an attempt is made to
erase the entire contents of the print job and cancel printing of the job. It is not
necessary to issue endJob after canceling a print job.

Once a print job is complete or has been canceled, an application can begin
another document by issuing startJob and beginning the printing process again.
When all print jobs are complete, destroyWidget should be used to destroy the
printer shell and release its associated resources. Finally, the printer display should
be closed if it is no longer needed.

CwPrinterShell resources and convenience methods


The following CwPrinterShell resource methods provide useful information related
to printing:
screen The printer screen on which the printer shell resides
height The read-only height of the printable area of a page, in pixels
width The read-only width of the printable area of a page, in pixels
x The read-only x-coordinate of the upper-left corner of the widget, in
relation to the printable area of a page
y The read-only y-coordinate of the upper-left corner of the widget, in
relation to the printable area of a page
title The document title, used by startJob to identify the current job
jobAttributes
The job attributes for the current print job
resolution
The read-only horizontal @ vertical resolution of the shell, in dots per inch
(DPI)
pageNumber
The current logical page number

The following CwPrinterShell convenience methods are also useful:


display
The CgDisplay on which the printer shell created.
window
The CgWindow associated with the printer shell.

Tip: If necessary, device-specific data can be sent directly to a printer, one byte at a
time using the CwPrinterShell method sendRawPrinterData:.

Printing with Common Graphics


The drawing process for printing is basically the same as that for drawing on a
video display: Obtain a graphics context, and draw graphics and text on a
drawable using Common Graphics methods. In the case of a printer shell, the
drawable is the printer shells window.

Chapter 10. Common Printing 279


The major distinction between printed graphics and screen graphics is device
resolution. Printers generally have much higher print resolution than video
displays, so a pixel on a 300 DPI laser printer is much smaller than a pixel on a 72
DPI video display. Correspondingly, an image drawn on a video display appears
much smaller when printed. Because Common Graphics methods are pixel-based,
applications that require printing capability must adjust to the resolution of the
printing device. Scaling can be done by querying the printer shell resolution and
multiplying drawing coordinates by an appropriate factor.

An application requires a graphics context to draw graphics on a printer. A


graphics context created for a printer is similar to one created for a video display,
with the limitation that a printer graphics context cannot generally be used for a
different printer, or even a different print job on the same printer. This is because
printer graphics contexts are printer- and print job-specific. For the same reason, a
printer graphics context cannot be used for video display play graphics. Also,
printer display graphics contexts are reset at the start of each page by the
operating system. For this reason, the application must reset any desired graphics
context fields in the expose callback before drawing.

Applications should send createGC:values: to a CwPrinterShell window to obtain a


graphics context for a printer. It is possible to create more than one graphics
context for a print job. default does not return a graphics context suitable for use on
a printer.

The font model for printing is the same as that for screen graphics. However,
printer fonts generally work only on printers and not on video displays. In
addition, video display fonts do not necessarily work on printers, although in
practice many do. The application can determine which fonts are supported by
both the video and printer displays by comparing the font information returned by
listFonts:maxnames: for each display.

Common Printing does not support cursors, because cursors are screen-specific
objects. Printed image support (including that for CgIcon) is provided via
CgDeviceIndependentImage. Common Printing does not support CgPixmap, because
pixmaps are device-specific. Applications must take printer resolution into account
when scaling images for the printer.

A complete example
The following example prints two pages of text, diagonal lines, and filled circles.
The printer where the pages are being sent is prompted for, and if there are no
printers available or if no printer is chosen, no printing is done.
Object subclass: PrintingExample
instanceVariableNames: 'done gc fontStruct'
classVariableNames: ''
poolDictionaries: 'CwConstants CgConstants'

print
"Print two similar pages of text and graphics.

NOTE: This call must run in the UI process because it


runs its own event loop until printing is completed."

"To test, execute the following:


PrintingExample new print"
| prompter displayName display jobAttributes printerShell |

done := false.

280 IBM Smalltalk: Programmers Reference


CgDisplay allPrinterDisplayNames isEmpty
ifTrue: [|System errorMessage: 'There are no available printers.'].
prompter := CwPrinterPrompter new.
(displayName := prompter prompt) isNil
ifTrue: [|nil].
jobAttributes := prompter jobAttributes.
display := CwAppContext default
openDisplay: displayName.
display isNil
ifTrue: [|nil].
printerShell := CwPrinterShell
appCreateShell: self class name
applicationClass: nil
display: display
argBlock: [:w | w jobAttributes: jobAttributes].
printerShell
addCallback: XmNmapCallback
receiver: self
selector: #printerShellMap:clientData:callData:
clientData: nil;
addCallback: XmNexposeCallback
receiver: self
selector: #printerShellExpose:clientData:callData:
clientData: nil;
addCallback: XmNdestroyCallback
receiver: self
selector: #printerShellDestroy:clientData:callData:
clientData: nil.
printerShell realizeWidget.
"Printing starts on return to the event loop - so force event loop here."
[done] whileFalse: [CwAppContext default readAndDispatch].

printerShellMap: printerShell clientData: clientData callData: callData


"Create a graphics context, load a font, begin the print job,
and start the first page."
| fontNames display |
gc isNil
ifTrue: [
"Create a graphics context."
gc := printerShell window
createGC: None
values: nil.
"Load a font (the first courier font) for use in drawing strings."
display := printerShell display.
fontNames := display
listFonts: '*courier*'
maxnames: 1.
fontStruct := fontNames isEmpty
ifTrue: [display defaultFontStruct]
ifFalse: [display loadQueryFont: fontNames first].
].
printerShell
startJob;
startPage.

printerShellDestroy: printerShell clientData: aStream callData: callData


"Free resources allocated for printing."
done := true.
gc freeGC.
fontStruct = printerShell display defaultFontStruct
ifFalse: [fontStruct freeFont].
printerShell display close.

Chapter 10. Common Printing 281


printerShellExpose: printerShell clientData: clientData callData: callData
"Process the current page. If all pages are processed, end the print job
and destroy the printer shell."
| pageNum |
gc setFont: fontStruct fid.
pageNum := callData pageNumber.
self printPage: pageNum on: printerShell.
printerShell endPage.
"This example only prints two pages."
pageNum < 2
ifTrue: [
printerShell startPage.
]
ifFalse: [
printerShell
endJob;
destroyWidget.
].

printPage: pageNum on: printerShell


"Print some text and graphics on a printerShell."
| halfWidth halfHeight fontHeight lineNumber |

halfWidth := printerShell extent x // 2.


halfHeight := printerShell extent y // 2.
lineNumber := 1.
fontHeight := fontStruct height.
0 to: halfHeight by: 100 do: [:y |
printerShell window
drawLine: gc
x1: 0
y1: 0
x2: halfWidth
y2: y
].
printerShell y + fontHeight to: halfHeight + fontHeight
by: fontHeight do: [:y |
printerShell window
drawString: gc
x: 40
y: y
string: 'Line: ', lineNumber printString, ', page: ' ,
pageNum printString.
lineNumber := lineNumber + 1.
].
printerShell window
fillArc: gc
x: halfWidth
y: halfHeight
width: halfWidth // 2
height: halfHeight // 2
angle1: 0 * 64
angle2: 360 * 64.

Setting up printers and queues on UNIX platforms


To direct the output of the Common Printing subsystem to a printer, you must first
configure the system to recognize any available printers and queues. Printer
definitions are installed through the CwPrinterPrompter. Install printer definitions as
follows:
1. Open the printer prompter by executing the following code in a workspace:
CwPrinterPrompter new prompt

282 IBM Smalltalk: Programmers Reference


2. When the prompter opens, select Install. A Printer Installation dialog will
appear, listing any configured printers. Tihs list should contain one installed
printer, which may be used as an example. After installing any additional
printers, this printer can be deleted.
3. To install a new printer, select Add Printer. A dialog appears, listing the
available printer types and currently configured ports. A connection to a printer
is specified by a combination of a PostScript printer definition (PPD) and a port
definition.
4. Select Define New Port to create a new port definition. A dialog appears,
allowing you to edit port definitions. Type the port definition in the Edit Port
input area. Port definitions have the following format:
port=print_command

The port is any string which users will recognize as referring to a particular
printer. The print_command is the command used for sending output to the
printer port (for example, lp -d or lpr -P. For example, suppose you have two
printers, ADMIN and LAB. Your definitions might look like the following:
ADMIN=rsh bandit "lp -d ps"
LAB=lpr -Pgonzo

In this example, ADMIN refers to a printer connected to the system BANDIT,


so the print command is a remote shell command executed on BANDIT.
ADMIN is a PostScript printer attached to a queue named PS, so the command
lp -d ps is executed on BANDIT to print to ADMIN. LAB, however, prints to a
printer on the local system, so the print command executed locally to print to
LAB is lpr -Pgonzo, where GONZO specifies the defined PostScript printer
queue name.

Note: Some systems use lp -dqueuename to send output to the printer; others
use lpr -Pqueuename. You should specify whatever command is
appropriate to generate printed output on your system.
5. Select Add/Replace to add the new port in the list of current port definitions.
6. Repeat for each printer you want to send output to.

Note: To modify an existing port using the Printer Setup dialog, select the port
you want to modify and edit the port information in the Edit Port input
area, then select Add/Replace.
7. In the Printer Devices field, select the description that matches the printer you
are installing. If no description matches your printer, contact your printer
vendor for a PPD file.
8. Select the desired port in the Current Port Definitions list box and select Add
Selected. The new printer is now included in the list of currently installed
printers.
To close any dialog without accepting the changes, select Cancel. To close a dialog
and accept the changes, select Dismiss.

Configuring printer setup options


The Xprinter Printer Setup widget does the following:
v Reads default configuration information from file $HOME/.Xpdefaults, which is
your local setup information file. If this file is absent, it reads default
configuration information from Xpdefaults in the directory you specify as the
printPath in the abt.ini file.
v Presents this information to you and allows you to modify these defaults.

Chapter 10. Common Printing 283


The orientation, scale, and number of copies to be printed can be specified directly
using this widget. Additionally, the action area of the Printer Setup Widget
contains the following six buttons:

Button Description
Apply Provides changed configuration information to the application without
updating the default printer
Save Saves the current configuration information as your default printer
Reset Reloads the default configuration from $HOME/.Xpdefaults
Cancel Closes the dialog and aborts all configuration changes
Options Displays the options dialog box that allows you to select a different
printer setup
Install Displays the installation dialog box that allows you to add or remove
printer devices and printer ports

Selecting the Install button opens a dialog allowing you to add and remove printer
definitions.

To select a different printer or to change printer-specific properties, select Options.


If the Options button is disabled, ensure that Printer Specific is selected in the
outputTo: field.

Printer name, resolution, page size, and paper tray can be changed using this
dialog. Selecting the arrow button t the right of the field displays a list of valid
values from which you can choose. The values presented differ based upon the
particular printer selected. To save the configuration as the default, after you have
changed the printer option, select Save. Select Apply when finished to close the
printer prompter.

Printer configuration information


By default, printer configuration information is stored in the file Xpdefaults, in the
printer subdirectory (that is, opt/IBMvast/printing). The information in this file is
used by the printer prompter to allow selection and modification of currently
defined printers. This file can be treated as common and shared by multiple users;
it is important, however, to note the following:

The Xpdefaults file is read-only. When a user modifies the printer information in
this file using the printer prompter, the resulting information is stored in the users
home directory in the file .Xpdefaults. If a users home directory contains an
.Xpdefaults file, this file will always be used in preference to the common
Xpdefaults file. Each user, therefore, can have a different printer configuration. As
a result, it is necessary to rename the $HOME/.Xpdefaults file to Xpdefaults and
move the file to the printing directory for all users to have access to the newly
installed printers.

284 IBM Smalltalk: Programmers Reference


Chapter 11. IBM Smalltalk Virtual Machine API
The IBM Smalltalk Virtual Machine Application Program Interface (API) is a C
language interface that binds to the IBM Smalltalk virtual machine and enables
you to write C code that interfaces with Smalltalk code.

This chapter describes the IBM Smalltalk Virtual Machine API and includes the
following:
v Calling functions in other languages from IBM Smalltalk
v Calling IBM Smalltalk from other languages
v Writing user primitives

Who should read this chapter


This chapter is for developers needing to interface to other languages or write
custom primitive operations.

Some reasons to interface to other languages are:


v Low-level operating system interface requirements
v Performance-critical code or enhancements
v Preserving existing business-critical code

You can use either of two mechanisms for interfacing to C code:


v PlatformFunction
v User primitives

If you need to interface to existing code written in C or other languages (operating


system code, for example), you would use PlatformFunction, because it does not
require you to write any C code. You can interface to C code using
PlatformFunction completely from Smalltalk. PlatformFunction enables you to call
arbitrary code that knows nothing about objects.

User primitives enable you to write performance-critical code that is


Smalltalk-specific. This code is aware of Smalltalk objects. For this reason, these
primitives can be very fast, and you can use them to improve performance.

This chapter assumes a familiarity with the C programming language and with the
C compiler for the IBM Smalltalk platform.

Conventions
The conventions used in this chapter are as follows:
v All publicly available IBM Smalltalk functions and macros are prefixed with Es.
This prevents name conflicts with user-developed code that is linked with an
IBM Smalltalk virtual machine.
v The terms byte, word, and long refer to 8-, 16-, and 32-bit quantities,
respectively.
v C language code in text is in the example font, while Smalltalk code is in italics.
v All function descriptions are in C and have the following form:
returnType functionName(type arg1, type arg2, ...)

Copyright IBM Corp. 1994, 2000 285


The text that follows the function description is an explanation of the
function, its parameters, and return values. Following the explanation appear
paragraphs like this:
Error cases: This paragraph describes error conditions, if any, and any
assumptions made about parameters.
Smalltalk equivalent: This shows the equivalent operation in Smalltalk, if
any.
Side effects: This describes any side effects the function might have.

This format is used to describe everything, even though some of the functions are
actually implemented as macros. You can see examples of this presentation format
in Functions available in user primitives on page 320.

IBM Smalltalk C programming model


The file esuser.h must be included by any C file that interfaces to IBM Smalltalk.
This file resides in the include directory provided with the release. The include
directory should be included in the C compiler search path by using the -I option.

Defined types
esuser.h defines the following C data types:
U_8, U_16, U_32
Unsigned 8-, 16-, and 32-bit integers
I_8, I_16, I_32
Signed 8-, 16-, and 32-bit integers
BOOLEAN
Either TRUE or FALSE (also defined)
EsObject
Pointer to an IBM Smalltalk object
EsVMContext
Pointer to a low-level process information block
EsBehavior
Pointer to a kind of Behavior (for example, Class or Metaclass)
EsGlobalInfo
Information global to all processes
EsPrimitiveTable
An IBM Smalltalk user primitive table

Object types
The five types of IBM Smalltalk objects include:
v Immediate objects
v Pointer objects
v Byte objects
v Word objects
v Long objects

In addition, all of these object types except immediate can be marked as one of
the following:
v Read-only
v Fixed

286 IBM Smalltalk: Programmers Reference


Immediate objects
An immediate objects value is represented directly in the object pointer, with no
data associated. In IBM Smalltalk, true, false, nil, Character, and SmallInteger are
immediate. In C, true, false, and nil can be referenced as EsTrue, EsFalse, and
EsNil, respectively.

IBM Smalltalk SmallIntegers range from -1073741824 to +1073741823.

Character values range from 0 to 65535.

Pointer objects
A pointer objects instance variables or indexed slots contain references to other
objects. An example of this type of object is Array.

Byte, word, and long objects


Byte, word, and long objects have no named instance variables. The indexed slots
contain bytes, word, or longs, respectively.

Read-only objects
Pointer, byte, word, and long objects can be marked read-only. A read-only object
cannot be stored into by Smalltalk.

The following methods relate to read-only objects:


markReadOnly: aBoolean
If aBoolean is true, marks the receiver as read-only. If aBoolean is false, marks
the receiver as read-write. Answers true if the receiver is now read-only,
false if the receiver is now read-write.
isReadOnly
Answers true if the receiver is read-only, false if not.

Note: For immediate objects in ROM, this method will always answer true
because these objects are always read-only.
attemptedROMStore: storedObject intoSlot: anInteger
Sends this message to a read-only object when an attempt is made to store
into it. anInteger is the instance variable index of the slot in the receiver
where the store was attempted (this is the same index used by
instVarAt:put:). storedObject is the object being stored.
If this method answers true, the store operation is retried. If the receiver
has been marked read-write, the store will take place. If not, this message
will be sent again. If this method answers false, the store operation is
ignored.

Note: storedObject and anInteger are suitable for instVarAt:Put: not at:put:. If
the receiver is a String or DBString, storedObject will be an Integer,
not a Character.

Fixed objects
Pointer, byte, word, and long objects can be made fixed. A fixed object cannot be
moved by the Smalltalk garbage collector. Fixed objects can be used to provide C
code with memory that can be used to store results (for example, a buffer for a
communications interrupt handler). Once an object has been made fixed, its
address does not change.

The following methods relate to fixed objects:

Chapter 11. IBM Smalltalk Virtual Machine API 287


makeFixed
Moves the receiver into fixed space. Answers the receiver. If there is not
enough fixed space memory for the receiver, a walkback occurs.
isInFixedSpace
Answers true if the receiver is fixed, false if not.

Note: Fixed objects that become garbage are not garbage collected until the next
time the image is started. At that time, IBM Smalltalk can be sure that there
are no C code references to the object and the object can safely be collected.

Macros versus functions


All operations in this chapter are described as being functions. However, some
might be implemented as macros. In future releases of IBM Smalltalk, some
operations might change from macros to functions or vice-versa. To ensure future
compatibility, do not pass expressions with side-effects to functions described here.
Also, do not attempt to take the address of any of the API functions.

External language interface


IBM Smalltalk interfaces with other languages through the PlatformFunction
mechanism. A PlatformFunction is an object that prototypes a function in another
language (C for example). The PlatformFunction contains the shared library where
the function can be found, the functions name or number, and the types of the
arguments and return value, if any.

For example, to create a PlatformFunction for the DosBeep function in OS/2, do the
following:
dosBeep :=
PlatformFunction
callingConvention: 'c'
function: 286
library: 'DOSCALL1'
parameterTypes: #(uint32 uint32)
returnType: #uint32.

See Platform requirements on page 333 for details about platform-specific calling
conventions. The function: parameter can be a positive Integer or a String. The
library: parameter is the name of the shared library where the function is stored. It
can be a String or nil. The parameterTypes: argument is an array of type names (see
below). The returnType: argument is a single type name.

If the library is given as nil, the function must be a String. The function is found in
the user primitive table instead of in a shared library. The string is the name of the
user primitive. The function that is called is not a user primitive; it is a normal C
function. See User primitive tables on page 319 for details on how to add
functions to the table.

Parameter types
The supported parameter types for the C language are:
none, void
This is not a valid parameter type. If used as the return type, the value nil
is returned.
object
No conversion is performed. The parameter value is passed directly to the

288 IBM Smalltalk: Programmers Reference


called function. Returned values are passed directly back to Smalltalk. The
PlatformFunction author must ensure that only valid IBM Smalltalk objects
are returned.
struct Can be used only as a parameter type; for parameters, this is valid only for
byte, word, or long objects and nil. For byte, word, and long objects, the
passed parameter is a pointer to the first instance variable of the object. For
nil, 0 (NULL) is passed.
char, char8
If used as a parameter type, the parameter must be a Character whose
numeric value is between 0 and 255. The parameter is converted to a C
char. If used as a return value, the low 8 bits of the return value are
converted to a Character before returning to Smalltalk.
char16 All instances of Character are converted. This means that the passed-in
values will be between 0 and 65535 and the low 16 bits of the return value
are converted to a Character on return.
float, double, extended
If used as a parameter type, the parameter must be a Float. The parameter
is converted to the specified size of float. If used as a return value, the
result is converted to an instance of Float.
bool, boolean
For outgoing parameters, only true or false can be converted. The passed
parameter is TRUE for true, FALSE for false. As a return type, 0 is
converted to false, and all other values are converted to true. The passed
value for true is platform-dependent. The value of !0 in C is passed.
int8, int16, 1nt32, uint8, uint16, uint32
If used as a parameter type, the parameter must be an Integer that can be
represented in 32 bits, Character, Boolean, or nil (see Passed parameters on
page 290). Instances of OSObject with reftype immediate (namely true, false,
nil, Characters, and SmallIntegers) can also be used (see OSObjects on
page 308). If used as a return type, the low n (8, 16, 32) bits of the return
value are sign- or zero-extended (signed or unsigned, respectively) and
then converted to a Smalltalk Integer. The return value in Smalltalk is
guaranteed to be within these given ranges:
int8 -128...127
int16 -32768...32767
int32 -2147483648...2147483647
uint8 0...255
uint16
0...65535
uint32
0...4294967295
pointer
As a parameter type, this is an amalgamation of the struct, char, bool, and
int type conversions. If the parameter is a byte, word, or long object or nil,
the struct conversion is performed. If the parameter is true or false, the bool
conversion is performed. If the parameter is a Character, the char16
conversion is performed. If the parameter is an Integer, the uint32
conversion is performed.
Instances of OSObject can also be supplied as the parameter. If the
OSObject reftype is handle, the handle is dereferenced and the offset is
added. All other objects cause the conversion to fail. If used as a return
type, the uint32 conversion is performed.

Chapter 11. IBM Smalltalk Virtual Machine API 289


safePointer
As a parameter type, the parameter may be a byte, word or long object.
Instances of OSObject can also be supplied as the parameter provided they
do not have reftype immediate. If used as a return type, the uint32
conversion is performed.

Passed parameters
All values passed to C functions are extended to a 32-bit quantity before being
passed. Signed integers are sign extended; unsigned integers are zero extended.
Because the C compiler does the same thing, it will be transparent to you.

All of the int types perform the same conversion for when converting from IBM
Smalltalk objects to external language values. For example, it is legal to pass -1 as a
uint32 or a uint8. Both result in 0xFFFFFFFF being passed.

Calling a PlatformFunction
Once a PlatformFunction has been created, it can be called using call, callWith:,
callWith:with:, and so on. The number of arguments in the call must match the
number of arguments in the PlatformFunction.

For example, to call the DosBeep function described above, you might use the
following statement:
rc := dosBeep callWith: 100 with: 200

Error cases
The call to a PlatformFunction can fail and cause a walkback for the following
reasons:
Invalid argument count
The number of parameters in the PlatformFunction is not the same as the
number of parameters in the call.
Invalid class
The parameter could not be converted because its class is invalid (for
example, trying to pass a Float in an int32 parameter). All such type
conversions must be done explicitly (for example, pass the Float truncated
as the parameter).
Value out of range
The Character or Integer value is not in the range allowed for the type (see
above for the ranges).
Not enough memory
There is insufficient memory to allocate a LargeInteger for the return value
of the function.
Operating system error
An operating-system-specific error occurred; for example, it could not find
the shared library or the function in the library.

Note: Calling a PlatformFunction with invalid arguments may result in a system


crash. You need to ensure the validity of the arguments.

Inline external function calls


An external language function can be called directly from a method using the
following syntax:

290 IBM Smalltalk: Programmers Reference


messagePattern
<callingConvention: returnType 'library':function parameterTypes>
...fail code...

For example:
send: msg to: id
<c: int32 'message':sendTo int32 int32>
|self primitiveFailed

External language functions have the same syntax as those in PlatformFunction,


with one exception. If the function called is in the user primitive tabe and not in a
shared library, omit the references to the library. Thus, change the previous
example as follows:
send: msg to: id
<c: int32 sendTo int32 int32>
|self primitiveFailed

The receiver of the method is not used in the function call. The number of
arguments to the method must be the same as the number of parameters specified
in the external function call. If callingConvention: specifies a returnType of void
or none, the Smalltalk method returns the nil.

If the call to the external function fails (for the reasons described in Error cases
on page 290), the fail code (the rest of the code in the method) is run. If no fail
code is provided, self is returned on failure. The suggested default fail code is |self
primitiveFailed. It generates a walkback describing the reason for the failure and
displaying the operating system error.

PlatformFunction protocols
The address of a PlatformFunction is not looked up until required (for example,
when calling it). Looking up the address is referred to as binding. A
PlatformFunction can also be unbound by the system, meaning that it must be
bound again before being called.

Instance methods: accessing


The get methods are listed below. For every get method, there is a corresponding
set method.
address Answers the address (a positive Integer) of the receiver. If the receiver is
not already bound, bind it. If the binding fails, a walkback occurs.
callingConvention
Answers the calling convention (a Symbol) of the receiver.
functionNumber
If the receiver is a numbered function, answers the number (a positive
Integer). Otherwise, answers nil.
library
If the receiver resides in a shared library, answers the appropriate
PlatformLibrary object. Otherwise, answers nil.
name If the receiver is a named function, answers the function name (a String).
Otherwise, answers nil.
parameterTypes
Answers an array of the receivers parameter types (Symbols).
returnType
Answers the Symbol describing the return type of the receiver.

Chapter 11. IBM Smalltalk Virtual Machine API 291


Instance methods: calling
call Calls the receiver and answers the result of the call. If the return type is
void, answers the receiver. If the receiver cannot be bound or a parameter
cannot be converted, a walkback occurs.
callWith:
Calls the receiver with the given arguments. Answers the result of the call.
If the return type is void, answers the receiver. If the receiver cannot be
bound or a parameter cannot be converted, a walkback occurs.
callWith:with:
Calls the receiver with the given arguments. Answers the result of the call.
If the return type is void, answers the receiver. If the receiver cannot be
bound or a parameter cannot be converted, a walkback occurs.
callWith:with:...with:
Calls the receiver with the given arguments. The maximum number of
arguments is 32. Answers the result of the call. If the return type is void,
answers the receiver. If the receiver cannot be bound or a parameter cannot
be converted, a walkback occurs.

Class methods: instance creation


callingConvention:function:library:parameterTypes:returnType:
Answers a new PlatformFunction with the given parameters. The
callingConvention: must be a string or symbol. The function: can be a
positive Integer or a String. The library: is the name of the shared library
where the function is stored. It can be a String or nil. parameterTypes: is an
array of type names (Strings or Symbols). The returnType: is a single type
name (a String or Symbol).

Note: If the library is given as nil, the function must be a String. The
function is looked up in the user primitive table instead of in a
shared library. The String is the name of the user primitive. The
function that is called is not a user primitive; it is a normal C
function.
callingConvention:address:parameterTypes:returnType:
Answers a new PlatformFunction with the given parameters. The
callingConvention:, parameterTypes:, and returnType: are the same as above.
The address: is a positive Integer. Use this method to create a
PlatformFunction for a function that is not in a shared library but whose
address is available by some other means (for example, returned by an
operating system call). A PlatformFunction created in this manner has its
address set to zero when the image starts.

Calling OS/2 Windows 95/98 Windows NT UNIX


Convention
c __stdcall __stdcall __stdcall (none)
pascal16 _Far16 _Pascal __far __pascal
pascal _Pascal
cdecl16 _Far16 _Cdecl __far __cdecl
v abtSystem and abtC32 are obsolete synonyms for c.
v c16 and abtsystem16 are obsolete synonyms for pascal16.
v abtpascal32 is an obsolete synonym for pascal.
v abtc16 is an obsolete synonym for cdecl16.

292 IBM Smalltalk: Programmers Reference


PlatformLibrary protocols
Instances of PlatformLibrary are used to look up function or data addresses in
operating system shared libraries. PlatformLibrary is used implicitly by instances of
PlatformFunction.

Each instance has a logical name and a physical name. The logical name is the
name used to refer to the library in Smalltalk (for example, the library name used
in a PlatformFunction). The physical name (which can be the same as the logical
name) is the name that is passed to the operating system. The PlatformLibrary class
keeps a list of all instances that it creates. This way, multiple instances with the
same logical name are never created.

Instance methods: accessing


logicalName
Answers the logical name of the receiver (a String).
physicalName
Answers the physical name of the receiver (a String or nil).

Instance methods: library operations


getAddress:
Answers the address (a positive Integer) of the library object in the receiver.
The library object must be a String or an Integer. If the library object cannot
be found, a walkback occurs.
close Closes the receiver. All PlatformFunctions that reside in the receiver are
unbound.

Class methods: mapping logical names to physical names


mapLogicalName:toPhysicalName:
Sets the mapping for the platform librarys logical name to the specified
physical name. If a PlatformLibrary with the specified logical name exists,
closes it and sets its physical name as indicated. If a new instance of
PlatformLibrary with the logical name is created at some later time, its
physical name is set as indicated. The logical name and the physical name
must be a String. The physical name must be either a String or nil. If the
physical name is nil, all functions in the library are looked up in the user
primitive table instead of an operating system shared library.
removeMappingForLogicalName:
Removes the mapping for the logical name, if any. If a PlatformLibrary with
the logical name exists, closes it, and sets its physical name to the logical
name. If a new instance of PlatformLibrary with the logical name is created
at some later time, its physical name is set to the logical name. The logical
name must be a String.

Class methods: instance creation


logicalName:
If a PlatformLibrary with the logical name already exists, answers it.
Otherwise, answers a new instance whose logical name is as indicated and
whose physical name is set according to the mappings. The logical name
must be a String.

Chapter 11. IBM Smalltalk Virtual Machine API 293


Class methods: miscellaneous
removeUnreferencedLibraries
Closes all instances of PlatformLibrary. Discards any instances that are not
referenced from at least one PlatformFunction. This method does not
remove any mappings.

Entry points
IBM Smalltalk allows other languages to call Smalltalk through the EsEntryPoint
mechanism. An EsEntryPoint is an object that prototypes a function in another
language such as C. An EsEntryPoint contains the Smalltalk receiver and selector as
well as the types of the parameters and any return value.

For example, to create an EsEntryPoint for the WindowProc function in Windows,


you can use the following code in the class OSEventManager:
initializeWindowClass
"Private - Get the standard window procedure and register the
window class in the OS."
| windowProc address |
"Get the address of the window proc and install the receiver and selector.
This message is sent from the window proc for every OS message."
windowProc :=
EsEntryPoint
receiver: self
selector: #windowProc:msg:with:with:
callingConvention: 'c'
arrayBased: false
parameterTypes: #(uint32 uint32 uint32 uint32)
returnType: #int32.
windowProc failAddress: DefWindowProc address.
address := windowProc address.
"Register the Smalltalk window procedure."
WindowClass := 'WINDOWPROC'.
Hab
winRegisterClass: WindowClass
pfnWndProc: address
flStyle: CsSizeredraw
cbWindowData: 4.

To use an EsEntryPoint, create it and send it the message address. The result is an
integer that can be passed out to a PlatformFunction that requires a function pointer
as a parameter. When the external language calls the function pointer, IBM
Smalltalk converts the parameters into Smalltalk objects and sends the message
designated by the receiver and selector of the EsEntryPoint. The receiver parameter
can be any Smalltalk object. The selector parameter must be a Symbol.
parameterTypes is an array of type names and returnType is a single type name. The
callingConvention parameter must be a string that is one of the valid IBM Smalltalk
calling conventions. For details about platform-specific calling conventions, see
Platform requirements on page 333.

There are two kinds of EsEntryPoints, depending on the value of arrayBased


parameter when an EsEntryPoint is created. If arrayBased is false, the selector must
take the same number of parameters as the number of parameters in the
parameterTypes array; that is, one per external language parameter. If arrayBased is
true, the selector must take one parameter, which is an array of all the parameters.

Note: When using an EsEntryPoint, you do not need to make any of the objects
(the receiver, selector, or the EsEntryPoint itself) fixed.

294 IBM Smalltalk: Programmers Reference


Parameter types and return types
The supported parameter types and return types are:
none, void
This is not a valid parameter type. If it is used as the return type, the
return value of the Smalltalk message is ignored and no value is returned
to the caller of the EsEntryPoint.
char, char8
If it is used as a parameter type, the low eight bits of the parameter are
converted to a Character whose numeric value is between 0 and 255. If it is
used as a return type, the return value from the Smalltalk message must be
a Character whose numeric value is between 0 and 255.
char16 If it is used as a parameter type, the low 16 bits of the parameter are
converted to a Character. If it is used as a return type, the return value
from the Smalltalk message must be a Character.
bool, boolean
If it is used as a parameter type, the low eight bits of the parameter are
tested for 0. If they are 0, false is passed to the Smalltalk message. If they
are not 0, true is passed. If it is used as a return type, only true or false can
be converted. The return value is TRUE for true, and FALSE for false.
The return value for true is platform-dependent. In C it is the value of !0.
int8, int16, int32, uint8, uint16, uint32
If it is used as a parameter type, the low 8, 16 or 32 bits of the parameter
are converted to an Integer that is in the specified range. If it is used as a
return type, the return value must be an Integer (see Returned
parameters on page 296). The integer ranges are as follows:
int8 -128...127
int16 -32768...32767
int32 -2147483648...2147483647
uint8 0...255
uint16 0...65535
uint32 0...4294967295
struct If it is used as a parameter type, the parameter is converted using uint32
conversion. If it is used as a return type, the return value must be a byte,
word, or long object or a non-immediate OSObject. nil is returned as 0.
pointer
If used as a parameter type, the parameter is converted using uint32
conversion. If it is used as a return type, this is an amalgamation of struct,
char, bool, and int type conversions. For a given parameter type, the
conversion performed is as follows:
byte object, word object, long object, nil
struct
true, false
bool
Character
char16
Integer uint32
OSObject
struct

All other objects cause the conversion to fail.

Chapter 11. IBM Smalltalk Virtual Machine API 295


safePointer
If it is used as a parameter type, this is the same as pointer conversion. If it
is used as a return type, the parameter can be a fixed byte, word, or long
object. Instances of OSObject can also be supplied as the parameter
provided they are non-immediate.
If an OSObject whose reference is a byte, word, or long object is returned,
the reference must be in fixed space. This is also true for byte, word, or
long objects that are returned directly.

Returned parameters
All values returned to external language functions are extended to a 32-bit quantity
before being returned. Signed integers are sign-extended; unsigned integers are
zero-extended. All the int types perform the same conversion when converting
from IBM Smalltalk objects to external language values. For example, -1 can be
returned as a uint32 or a uint8. Both result in 0xFFFFFFFF being returned.

Calling an EsEntryPoint
Once an EsEntryPoint is created, send it the message address to bind it. The
address can be passed to an external language function that requires a function
pointer. For example, to get the address of the WindowProc callback described
above:
address := windowProc address.

External functions calling Smalltalk using EsEntryPoint must run in the same
thread as Smalltalk.

Error cases
The call to address can fail and cause a walkback for the following reasons:
No more entry points
All available EsEntryPoint addresses are in use.
Invalid calling convention
The EsEntryPoint is using an unsupported calling convention.
Not enough memory
There is insufficient memory to allocate a LargeInteger for the return value
of the function or to allocate internal support structures.

EsEntryPoint failure
When the address of an EsEntryPoint is called by the external language, there is
always the possibility of failure. It may not be possible to allocate the necessary
objects required for the conversion to Smalltalk objects. The return value from
Smalltalk may be invalid and unconvertable.

EsEntryPoint instances contain a fail address that defaults to 0. When the callback
fails for any reason, this address is checked. If it is 0, 0 is returned from the
callback. If it is not 0, it must be the address of a function that has exactly the
same prototype as the EsEntryPoint. Upon failure, this function is called.

For example, in Windows, the following code can be used to catch a WindowProc
failure:
windowProc failAddress: (PlatformFunctions at: 'DefWindowProc') address

This code causes DefWindowProc to be called when the Smalltalk WindowProc


fails. When an EsEntryPoint unbinds, the fail address is reset to 0.

296 IBM Smalltalk: Programmers Reference


EsEntryPoint protocols
EsEntryPoint has the following methods:

Instance methods: accessing


address Answers the address (a positive integer) of the receiver. If the receiver is
not already bound, binds it. If the binding fails, a walkback occurs.
failAddress
Answers the fail address (a positive integer) of the receiver. Answers 0 if
no fail address has been specified.
failAddress:
Sets the fail address (a positive integer) of the receiver to anInteger. If
anInteger is not valid, a walkback occurs.
receiver
Answers the receiver of the callback message selector. Answers the selector
of the callback message.
isArrayBased
Answers true if the received accepts arguments as an array; false if the
receiver accepts each argument individually.
callingConvention
Answers the calling convention (a Symbol) of the receiver.
parameterCount
Answers the number of arguments expected by the receiver.
parameterTypes
Answers an array of the receivers argument types (instances of Symbol).
returnType
Answers the Symbol describing the return type of the receiver.

Instance methods: miscellaneous


unbind Unbinds the receiver. This returns the receivers address to the pool of
available addresses. The receiver must not be in use when it is unbound or
the system may crash the next time the address is called. This also resets
the fail address of the receiver to zero. All instances of EsEntryPoint and its
subclasses are unbound when the image starts.

Class methods: instance creation


receiver:selector:callingConvention:arrayBased:parameterTypes:returnType:
Answers a new EsEntryPoint with the given parameters. The
callingConvention must be a String or Symbol. The receiver parameter may
be any Smalltalk object. The selector parameter must be a Symbol.
parameterTypes is an array of type names and returnType is a single type
name.
There are two kinds of EsEntryPoints, depending on the value of arrayBased
parameter when the EsEntryPoint was created. If arrayBased is false, the
selector must take the same number of parameters as the number of
parameters in the parameterTypes array; that is, one per external language
parameter. If arrayBased is true, the selector must take one parameter, which
is an array of all the parameters.

Chapter 11. IBM Smalltalk Virtual Machine API 297


Asynchronous callouts
When a platform function call is made in Smalltalk, all Smalltalk processes block
until that call completes. Asynchronous callout is an extension to the standard
platform function call protocol that allows developers to make a platform function
call in a separate thread. By making the call in a separate thread, it ensures that
only those processes waiting on the result of the platform function call block.

For example, if a socket receive call that blocks is made in Smalltalk, no Smalltalk
processes run until the receive call completes. By using asynchronous callout, only
those processes waiting on the return value of the receive operation block.

These are the ways a Smalltalk process makes asynchronous calls:


Standard asynchronous call
The Smalltalk process blocks until the call completes. This type of call is
used when the process needs the return value and cannot proceed until it
has been calculated.
Resource future call
The Smalltalk process receives a future and continues to run, polling the
future for the result of the platform function call. This type of call is used
when the process does not immediately need the return value and can
continue executing. Additionally, more than one process may wait on the
return value.
Static future call
The Smalltalk process receives a future and continues to run, polling the
future for the result of the platform function call. A static future call differs
from a resource future call in that the same thread can be used by more
than one call. Some calls, such as those that get the last error, require
several platform function calls to be made in the same thread.

The syntax for using asynchronous calls is similar to normal platform function
calls. The system sets up the OS resources required to make the call, executes the
call, and recovers the resources automatically.

With resource futures calls, the system manages the OS resources. The system sets
up the OS resources required to make the call, executes the call, and recovers the
resources automatically. Resource future calls can be used for single asynchronous
calls only.

Static futures calls require the developers to manage resources themselves. The
advantage of a static future call is that it can be used to make more than one
platform function call in the same thread. The developer must explicitly allocate
the resources for the future and return them when the future is no longer required.

Calling a Platform Function asynchronously


After a PlatformFunction has been created, it can be called using one of these
asynchronous protocols:
Standard asynchronous call
asyncCall, asyncCallWith:, asyncCallWith:with:, and so on
Resource future call
futureCall, futureCallWith:, futureCallWith:with:, and so on

298 IBM Smalltalk: Programmers Reference


Static future call
staticFutureCall:, staticFutureCall:with:, staticFutureCall:with:with:, and so on

The number of arguments in the call must match the number of arguments in the
PlatformFunction. The following sections describe how to make asynchronous calls
using each of the protocols.

Standard asynchronous calls


Standard asynchronous calls are created using the asyncCall... API. Use standard
asynchronous calls when the value is required by a single process, and the process
must wait until the call has completed. Standard asynchronous calls cause the
current Smalltalk process to be suspended until the value is returned. Other
Smalltalk processes continue to execute.

When an asynchronous call is made, an operating system thread is acquired, the


current Smalltalk process suspends, and the platform function is executed in that
thread. When the call completes, the result is posted back to Smalltalk, and the
process that was waiting for the result resumes.

For example, to call a platform function asynchronously:


param1 := 100.
param2 := 200.
rc := dosBeep asyncCallWith: param1 with: param2

param1 and param2 are the arguments to the call. dosBeep is a previously declared
platform function. The rc is the return value of the platform function, or an
AcoError if an error occurred during the execution of the call. For a discussion of
errors, see ACO errors and error cases on page 301.

When a standard asynchronous call is made during a system callback (that is when
the operating system has called back into Smalltalk), the call is performed
synchronously. Standard asynchronous calls are the fastest form of asynchronous
call.

Resource future calls


Resource future calls should be used if more than one process needs to wait on the
return value or if the current process must continue executing until the return
value is needed.

When a resource future call is made, a future is returned. This future can be
queried for the state of the call while the platform function executes in a separate
thread. If a process asks for the value of the future before the value is ready, the
process is suspended. More than one process can wait on the return value of the
future. When the call completes, the result is posted back to Smalltalk and the
processes that are waiting on the result are resumed.

For example, to call a platform function using a resource future call:


param1 := 100.
param2 := 200.
aFuture := dosBeep futureCallWith: param1 with: param2.
12 fibonnacci. "May or may not execute before the call completes."
rc := aFuture value.
12 fibonnacci. "Will not execute until the call completes."

param1 and param2 are the arguments to the call. The rc is the return value of the
platform function, or an AcoError if an error occurred. The aFuture is a future that
contains the result of the platform function. You must explicitly ask the future for

Chapter 11. IBM Smalltalk Virtual Machine API 299


its value. If the platform function has not yet completed, the process asking for the
value of the platform function call blocks until the value is ready.

To check the status of a blocking call, the messages checkForValue or isReady can be
sent to the future. The message checkForValue answers the return value if it is ready
or an AcoError if it is not yet ready. The message isReady answers true if the return
value is ready; otherwise, it answers false. When a future is asked for its value
during a system callback (that is when the operating system has called back into
Smalltalk), if the return value has not been calculated, then an AcoError is
answered. Resource future calls are slower than standard asynchronous calls.

Static future calls


Like resource future calls, static future calls allow the current Smalltalk process to
continue running until the return value is needed.

In addition, static future calls allow you to use the same operating system thread
in more than one asynchronous call. The static future call does this by reserving a
thread. This is useful when data is stored in the local memory for a thread. As a
result, you must manage the resources by explicitly creating and destroying static
futures and their resources. Allocating resources for static futures on page 301
discusses the allocation and deallocation of resources.

To begin a static future call, the developer creates a static future and makes the
call. When the call completes, the result is posted back to Smalltalk and processes
waiting on the result are resumed. At this point, the developer may either reuse
the static future or return its resources to the AcoResourceManager.

For example, to call a platform function using a static future call:


aFuture := AcoResourceManager default createStaticFuture.
param1 := 100.
param2 := 200.
rc := dosBeep staticFutureCall: aFuture with: param1 with: param2.
rc := aFuture value.
"A second call can be made using the same thread."
rc := dosBeep staticFutureCall: aFuture with: param1 with: param2.
rc := aFuture value.
AcoResourceManager default returnStaticFuture: aFuture.

param1 and param2 are the parameters to the call. The rc is the return value of the
platform function. The aFuture is a static future that contains the result of the
platform function. You must explicitly ask the future explicitly for its value. If the
platform function has not completed, the process asking for the value of a future
blocks until the value is ready.

The static future can be reused for other platform function calls. These calls will be
made in the same operating system thread. Once the developer no longer requires
the static future, it must be returned to the AcoResourceManager.

To check the status of a blocking call, the messages checkForValue or isReady can be
sent to the future. The message checkForValue answers the return value if it is ready
or an AcoError if it is not ready. The message isReady answers true if the return
value is ready; otherwise, it answers false.

Before a static future can be used in a subsequent call, the previous call must have
completed. There are two protocols for determining this. Sending value to the
future guarantees that the previous call has completed before the next starts

300 IBM Smalltalk: Programmers Reference


because value waits until the call completes. When a static future is sent isAvailable,
it answers true if the future is not involved in another asynchronous call and false
if it is.

When a future is asked for its value during a system callback (that is when the
operating system has called back into Smalltalk), if the return value has not been
calculated, then an AcoError is answered.

Static future calls are slower than standard asynchronous calls, but faster than
resource future calls.

Allocating resources for static futures: The AcoResourceManager allocates and


deallocates resources for a static future. Resources include the thread used to make
the call and some fixed-space memory. To get the default resource manager, the
message default is sent to the AcoResourceManager class. The default resource
manager can then be sent the following messages to allocate and deallocate
resources:
createStaticFuture
Allocates a static future and the required resources. Answers the static
future created.
returnStaticFuture:
Returns to the resource manager the resources used by the static future.
reinitializeStaticFuture:
Reacquires resources for the static future that were previously lost when
the image was saved.
createStaticFutureWithStackSize:
Creates a static future with a thread having a stack size equal to anInteger.
Answers the static future created.

Locking resources for an asynchronous call


Occasionally, several calls must be made in the same thread. For example, in
Windows to get the last error code from a function call, developers must make the
GetLastError call in the same thread as the original call was made.

By using the lockThreadIn: aBlock protocol in the AcoResourceManager the developer


specifies a series of standard asynchronous calls (for example, asyncCall) to be
made in the same operating system thread. The protocol reserves a single thread
for all the calls made in the scope of the block.

The following is an example of standard asynchronous calls (dosBeep and


getLastError) that are guaranteed to occur in the same thread:
AcoResourceManager default lockThreadIn: [
rc := dosBeep asyncCallWith: param1 with: param2.
rc2 := getLastError asyncCall]

ACO errors and error cases


An asynchronous call can generate these types of errors:
Errors that generate walkbacks
These errors are caused by an incompatibility between the arguments and
the platform function call.
Errors that generate AcoErrors for the return value
These errors are often recoverable because they are often caused by

Chapter 11. IBM Smalltalk Virtual Machine API 301


resource problems associated with making the call. By returning an error
object rather than a walkback, the developer can recover from the failure
without using exceptions.

Walkbacks
The asynchronous call to a PlatformFunction can fail and cause a walkback for
several reasons. The reasons are identical to the error cases for callWith:, which are
described in Error cases on page 290.

ACO errors
The return value from an asynchronous call is either the return value of the
platform function or an AcoError; the AcoError identifies the problem that occurred
while making the asynchronous call.

To test for an error, isAcoError can be used. For example, to determine whether an
error occurred, the following code can be used.

For standard asynchronous calls:


rc := dosBeep asyncCallWith: parm1 with: parm2.
rc isAcoError ifTrue: [self error: rc printString]

For resource future calls:


future := dosBeep futureCallWith: parm1 with: parm2.
rc := future value.
rc isAcoError ifTrue: [self error: rc printString].

For static future calls:


staticFuture := AcoResourceManager default createStaticFuture.
staticFuture isAcoError ifTrue: [self error: staticFuture printString].
dosBeep staticFutureCall: staticFuture with: parm1 with: parm2.
rc := future value.
rc isAcoError ifTrue: [
AcoResourceManager default returnStaticFuture: staticFuture.
self error: rc printString].
AcoResourceManager default returnStaticFuture: staticFuture.

ACO errors include:


Resources invalid
The operating system resources (for example, the thread) are no longer
valid. This may occur after an image is shut down and restarted.
Invalid static future
The future being passed into a static future call is either not a type of
StaticFuture or is a static future that has invalid operating system resources.
Resources may become invalid after an image is shut down and restarted.
The resource is not available
There are no available resources.
The static future is not available
The static future being used to make the call is currently executing a
previous call.
Resources lost
An asynchronous call was in progress when an image was saved. When
the image restarted, the asynchronous call cannot continue because the
operating system resources (for example, the thread) are no longer valid.

302 IBM Smalltalk: Programmers Reference


The value is not available
The future has not completed the call and the return value is not ready.
Asynchronous callout not supported
The platform does not support asynchronous callout.
Primitive failed due to general protection fault
The platform function that was called caused a general protection fault.
Invalid stack size requested
A stack size was requested when a thread was locked using the
lockThreadIn: method of AcoResourceManager.
Requested future value in a system callback
You cannot request the value of a future during a callback into Smalltalk
from the operating system.

Parameter types and return types


All parameters and return types used in standard platform function calls are
permitted except:
object You cannot pass or return object parameters in functions that are called
asynchronously, including objects in fixed space.

Parameters passed with asynchronous calls


Objects located in movable memory that are passed into an asynchronous call are
copied into a location in non-movable memory for the duration of the
asynchronous call. When the asynchronous call completes, the arguments are
copied back to the original objects. If the object is not copied, the garbage collector
may move the object during the call, resulting in unpredictable behavior.

Objects that are not movable and not copied include:


v OSObjects that reside in operating system memory, such as those allocated using
calloc
v Objects that reside in fixed space

Objects are otherwise passed in the same way as platform function calls using the
callWith:... protocol.

Because objects are copied during an asynchronous call, the parameters passed to
the function should not be used while the asynchronous call is in progress. The
contents of the parameters may be changed at any time by the asynchronous call.

Managing resources
Asynchronous calls are made in a separate operating system thread. When the call
is blocked waiting for a resource, only that thread blocks and not the thread that
IBM Smalltalk is running in.

To support asynchronous callout, IBM Smalltalk manages both the thread and
some additional resources in fixed space. The additional resources allow IBM
Smalltalk and the asynchronous thread to interact.

Minimum required resources


To make asynchronous calls, the platform must support threads at the operating
system level. On platforms that do not support threads at such a level,
asynchronous calls are converted into synchronous calls.

Chapter 11. IBM Smalltalk Virtual Machine API 303


To determine whether a platform supports asynchronous callout, the class
EsAsynchronousCallout can be sent the message supported, which returns true if the
platform supports asynchronous callout and false if it does not.

Additionally, the image must have adequate fixed space allocated to allow
asynchronous calls to be made.

ACO resource manager


The resources (that is, the thread and fixed-space memory) are managed by the
ACO resource manager (AcoResourceManager). The manager improves performance
by caching resources that are expensive to create (the thread) or are not collectable
by garbage collection (the fixed-space memory).

When using standard asynchronous protocols (asyncCall...) or resource future


protocols (futureCall...), the resources are managed automatically through the
resource manager. Static future protocols (staticFutureCall:...) permit the resources to
be managed manually, but still require the resource manager.

The resource manager allows developers to control how operating system threads
and fixed-space resources are managed. The resource manager allows you to
control the resources using the following selectors:
cacheSize:, cacheSize
The number of resources to cache. The default is 10 threads.
maximumNumberOfResources:, maximumNumberOfResources
A maximum limit on the number of resources created. On some systems,
all the processes compete for a limited set of threads. This value is used to
limit the number of threads that Smalltalk acquires for itself. The default is
no limit.
limitOfReclaimedFutures:, limitOfReclaimedFutures
The number of futures that are recovered from fixed space when the image
starts up. The default is 10.
defaultStackSize:, defaultStackSize
The stack size that threads are created with. The default is 8192 bytes.
defaultPriority:, defaultPriority
The priority that operating system threads run at. This is a value between
1 (lowest priority ) and 7 (highest priority).

Resource limitations
An asynchronous call can have problems with resources by either:
v Losing a resource, for example, when a thread is killed
v Failing to acquire a resource

If resources are lost during a call, an AcoError is returned from the asynchronous
call indicating that the resources associated with the asynchronous call were lost.

Note: If you save an image, all the calls proceed normally in the running image.
However, when you load the saved image, the threads for the asynchronous
calls have been terminated, and the asynchronous calls indicate that they
have lost their resources.

The three different types of asynchronous calls can fail to acquire resources if one
of these conditions is true:
v The AcoResourceManager has reached the limit set by
maximumNumberOfResources:.

304 IBM Smalltalk: Programmers Reference


v The operating system cannot create additional threads.
v There is no more fixed space available.

Depending on the type of asynchronous call, there are different responses to the
failure to acquire resources. Calls using asyncCall or futureCall are automatically
queued, on a first-come, first-serve basis, until the resources become available. As a
result, standard asynchronous calls and resource future calls are delayed if there
are not enough resources. Calls using the staticFutureCall: method answer an
AcoError indicating that the resources cannot be allocated.

Maintaining a thread
The AcoResourceManager and AcoStaticFutures let the developer maintain the thread
an asynchronous call is made in. This is important for allowing more than one call
to be made in the same thread. For example, getting the last error is thread-specific
and must be guaranteed to occur in the same thread as the call that generated the
error.

AcoResourceManager lets you specify a set of calls to be made in the same thread,
using the lockThreadIn: aBlock protocol. All the calls using asyncCall performed in
aBlock are made in the same thread. The resources are reserved exclusively for calls
made in this block until the block finishes executing.

An AcoStaticFuture explicitly acquires and releases its threads. Between the time
that an AcoStaticFuture acquires a thread and releases it, its thread is reserved for
exclusive use by that future.

Instances of both AcoResourceFuture and AcoStaticFuture are unaffected by


lockThreadIn: aBlock. The resources they acquire and release are independent of the
resources held by the lock.

Extensions to platform function protocols


There are different calling conventions for making asynchronous calls, depending
on whether they are standard asynchronous calls, resource future calls, or static
future calls.

Instance methods: standard asynchronous


asyncCall, asyncCallWith:, asyncCallWith:with:, asyncCallWith:with:...with:
Call the receiver with the given parameters. The maximum number of
parameters is 32. Block the current process until the call is completed.
Answer the result of the call. If the return type is void, answer the receiver.
If the receiver cannot be bound or an argument cannot be converted, a
walkback occurs.
asyncCallWithArguments: anArray, asyncCallWithArguments: anArray stackSize:
aStackSize
Call the receiver with the given parameters. The maximum number of
parameters is 32. If aStackSize is requested, use a thread with the specified
number of bytes. Block the current process until the call is completed.
Answer the result of the call. If the return type is void, answer the receiver.
If the receiver cannot be bound or a parameter cannot be converted, a
walkback occurs.

Instance methods: resource future


futureCall, futureCallWith:, futureCallWith:with:, futureCallWith:...with:...with:
Call the receiver with the given parameters. The maximum number of
parameters is 32. Answer an AcoResourceFuture. The current process

Chapter 11. IBM Smalltalk Virtual Machine API 305


continues to run. To obtain the return value, ask the AcoResourceFuture for
its value. If the receiver cannot be bound or a parameter cannot be
converted, a walkback occurs.
futureCallWithArguments: anArray futureCallWithArguments: anArray stackSize:
anIntegerStackSize
Call the receiver with the given parameters. If aStackSize is requested, use a
thread with the specified number of bytes. The maximum number of
parameters is 32. Answer an AcoResourceFuture. The current process
continues to run. To obtain the return value, ask the AcoResourceFuture for
its value. If the receiver cannot be bound or a parameter cannot be
converted, a walkback occurs.

Instance methods: static future


staticFutureCall: anAcoStaticFuture staticFutureCall: anAcoStaticFuture with: parm1
staticFutureCall: anAcoStaticFuture with: parm1 with: parm2 staticFutureCall:
aAcoStaticFuture with: parm1...with: parmN...with: parm16 staticFutureCall: future
withArguments: anArray
Call the receiver with the given parameters. the maximum number of
parameters is 32. Answer the parameter anAcoStaticFuture. The current
process continues to run. To obtain the return value ask anAcoStaticFuture
the for its value. If anAcoStaticFuture is not a type of AcoStaticFuture or has
lost its resources, answer an AcoError. If the receiver cannot be bound or a
parameter cannot be converted, a walkback occurs.

ACO resource manager protocols


Class methods: resource control protocols
cacheSize
Answer the maximum number of resources that can be cached.
cacheSize:
Set the maximum number of resources that can be cached.
default Answer the default resource manager for the system.
defaultStackSize
Answer the default stack size of the thread.
defaultStackSize:
Set the default stack size (in bytes) of the thread.
defaultThreadPriority
Answer the default thread priority.
defaultThreadPriority:
Set the default thread priority.
limitOfReclaimedFutures
Answer the number of futures to be reclaimed when the image next starts
up. A reclaimed future is added back into the AcoResourceManager. If this
value is nil, all possible futures are added. Futures that are not reclaimed
remain in fixed memory until they are collected during garbage collection
the next time the image starts up.
limitOfReclaimedFutures:
Set the number of futures to be reclaimed when the image next starts up.
A reclaimed future is added back into the AcoResourceManager. If this value

306 IBM Smalltalk: Programmers Reference


is nil, all possible futures are added. Futures that are not reclaimed remain
in fixed memory until they are collected during garbage collection the next
time the image starts up.
maximumNumberOfResources
Answer the maximum number of resources that can be created. If this
value is nil, the resources are limited by the fixed-space size and number of
threads available from the operating system.
maximumNumberOfResources:
Set the maximum number of resources that can be created. If this value is
nil, the resources are limited by the fixed-space size and number of threads
available from the operating system.

Instance methods: static resource management protocols


createStaticFuture
Answer a static future that maintains its resources during each call.
Answer nil if failed to create a complete future because of lack of
resources.
createStaticFutureWithStackSize:
Answer a static future with the specified stack size that maintains its
resources during each call. Answer nil if failed to create a complete future
because of lack of resources.
reinitializeStaticFuture:
Answer the static future if resources can be acquired, nil otherwise. Discard
the resources that the static future is currently using and reinitialize it with
new resources. Use this method to reinitialize only static futures that are
needed after an image is exited and restarted because Smalltalk discards
the current resource.
returnStaticFuture: aFuture
Used to return a static future to the resource pool. Answer an AcoError if
the static resources cannot be returned to the pool; answer self otherwise.
lockThreadIn: aBlock
Evaluate all the asynchronous calls in the block aBlock using the same
thread. Answer the result of the block.

Resource future protocols


Instance methods
checkForValue
Answer true if the value is available and an AcoError otherwise.
isAcoFuture
Answer true if the object is a type of AcoFuture.
isReady
Answers true if the value is available; false otherwise.
isStatic Answer false. This is not a static future.
value Answer the value of the future. If the value has not been calculated,
suspend the current process until the value is ready.

Chapter 11. IBM Smalltalk Virtual Machine API 307


Static future protocols
Instance methods
checkForValue
Answer true if the value is available and an AcoError otherwise.
isAcoFuture
Answer true if the object is a type of AcoFuture.
isReady
Answers true if the value is available; false otherwise.
isStatic Answer true. This is a static future.
value Answer the value of the future. If the value has not been calculated,
suspend the current process until the value is ready.

ACO error protocols


Instance methods
gpInfo Answer the gpinfo string and nil if there was no general protection fault.
identifier
Answer a string describing the error identifier.
isAcoError
Answer true.
errorCode
Answer the error number.
message
Answer a string describing the error reported by the receiver.
printOn: aStream
Print a text representation of the receiver on aStream. The format is
<identifier>(<errno>: <message>.

OSObjects
Traditionally, Smalltalk has been ill-suited for modeling the complex memory
structures used by low-level languages like C, and by most modern operating
systems. Often programmers were required to build facilities for accessing these
structures using variable-byte classes and complex glue code. IBM Smalltalk, in
addition to having byte, word, and long classes for accessing simple array-like
memory structures efficiently, provides a mechanism for describing and accessing
arbitrary memory structures.

OSObject subclasses
Subclasses of class OSObject are used to model particular kinds of memory
references. OSObject closely models C-style addressing and structure sharing.

Every OSObject has an indirection level that describes how many times a pointer
must be followed to reach the actual data (a sequence of bytes in memory). The
operation of dereferencing a pointer results in another pointer or immediate data
and reduces the indirection level of the new pointer by one.

308 IBM Smalltalk: Programmers Reference


OSImmediate
You can use subclasses of OSImmediate to represent C-style typedefs to unsigned 8-,
16-, 32- or 64-bit values. Instances of OSImmediate cannot be dereferenced. All
subclasses of OSImmediate have indirection level zero. To model HWND (the C
typedef #typedef HWND unsigned long), add a new subclass of OSImmediate.
OSImmediate provides the inherited class instance variable fixedSize that must be
assigned before the class can be used. In this example, fixedSize is 4 (the size of
unsigned long).

OSBaseType
You use subclasses of OSBaseType to represent pointers to C base types.
Dereferencing an OSBaseType answers a Smalltalk immediate type. All subclasses of
OSBaseType have indirection level one. To model int * (a pointer to a C array of
int), use the class OSInt32. Dereferencing an instance of OSInt32 results in a signed
Smalltalk integer. IBM Smalltalk provides the following standard OSBaseType
subclasses:
v OSBool8, OSBool16, OSBool32
v OSChar8, OSChar16
v OSFloat32, OSFloat64
v OSInt8, OSInt16, OSInt32
v OSUInt8, OSUInt16, OSUInt32
v OSVoid

OSStructure
You use subclasses of OSStructure to represent arrays of structs and unions. All
subclasses of OSStructure have indirection level one. To model struct POINT * (a
pointer to a C structure struct {int x, y} POINT;), a new subclass of OSStructure
is required. OSStructure provides the inherited class instance variables fixedSize that
must be assigned before the class can be used. In this example, fixedSize is 8 (the
size of int x, y;).

OSVariableStructure
You use subclasses of OSVariableStructure represent variable-sized structures.
Dereferencing is disallowed because the size of each element is not known. To
model struct DATA * (a pointer to struct {int cbSize; char data[1]} DATA;), a
new subclass of OSVariableStructure is required. OSStructure provides the inherited
class instance variables fixedSize and variableSize that you must assign before the
class can be used. In this example, fixedSize is 4 (the size of int cbSize;) and
variableSize is 1 (the size of one data element in the variable-sized portion of the
structure, or char data [1];).

OSObjectPointer
OSObjectPointer is used to represent pointers to other instances of OSObject.
Dereferencing an OSObjectPointer answers a new instance of OSObjectPointer or an
OSObject subclass.

To model struct POINT ** (a pointer to a pointer to a C structure struct {int x,


y;} POINT;), a new instance of OSObjectPointer is created. The following example
uses an OSObjectPointer to model a pointer to an OSPoint:
| ptr |
ptr := OSObjectPointer calloc: 1 itemType: OSPoint.
ptr at: 0 put: (point := OSPoint calloc x: 12; y:13)

OSObjectPointer can point to any OSObject. You can also use subclasses of
OSObjectPointer to model pointers to a single type of OSObject. The class of this
OSObject is specified by assigning the defaultItemType class instance variable in the

Chapter 11. IBM Smalltalk Virtual Machine API 309


new OSObjectPointer subclass. For example, by defining OSPointPointer (a subclass
of OSObjectPointer) and assigning defaultItemType to OSPoint, the above code
fragment could be rewritten as:
| ptr |
ptr := OSPointPointer calloc: 1.
ptr at: 0 put: (point := OSPoint calloc x: 12; y:13)

OSObject protocols
This section describes the protocols supported by OSObject.

Instance methods
castTo: Answer a new instance of anOSObjectClass, which is initialized to refer to
the same data as the receiver using the appropriate indirection level.
isOSObject
Answer true if the receiver is an OSObject or one of its subclasses.
isUndefined
Answer true if the receiver has no value.
makeUndefined
Make the receiver undefined. OSObject and PlatformFunction primitives fail
when used with an undefined OSObject.

Class methods: initialization


fixedSize
Answer an integer that is the size in bytes of one data element of the
receiver.
fixedSize: anInteger
Set an integer that is the size in bytes of one data element of the receiver.
This value must be set before using an instance of the receiver.

Class methods: instance creation


undefined
Answer an undefined instance of the receiver.

OSImmediate protocols
This section describes the protocols supported by OSImmediate.

Instance methods
= Answer a Boolean indicating whether the receiver and anOSObject are
equal. Two immediates are equal if they have the same class and equal
values.
asInteger
Attempt to coerce the receivers value to an integer.
indirectionLevel
Answer the receivers indirection level.
isImmediate
Answer true if the receiver represents an immediate data element.
isNull Answer true if the receiver is a NULL value.
notNull
Answer true if the receiver is not a NULL value.

310 IBM Smalltalk: Programmers Reference


value Answer the receivers immediate data coerced into the most appropriate
Smalltalk class, as defined by the particular OSObject subclass. For
immediates, this is always an unsigned integer.

Class methods
immediate:
Answer a new instance of the receiver with indirection level 0, referencing
a data element with the same value as the parameter.
new Answer a new instance of the receiver with indirection level 0, referencing
0.
value: Same as immediate:.

OSBaseType, OSObjectPointer, and OSStructure protocols


This section describes the protocols supported by OSBaseType, OSObjectPointer, and
OSStructure.

Instance methods: pointer arithmetic


+ Answer a new instance of the receiver, with the same indirection level,
pointing to the data element at index anInteger from the data element
referenced by the receiver.
- Answer a new instance of the receiver, with the same indirection level,
pointing to the data element at index anInteger before the data element
referenced by the receiver.
decrement
Decrease the pointer by one to point to the previous data element.
decrementBy:
Decrease the pointer by anInteger to point to the previous data element.
increment
Increase the pointer by one to point to the next data element.
incrementBy:
Increase the pointer by anInteger to point to the next data element.

Instance methods: low-level storage access


These methods enable you to access the contents of an OSBaseType, OSStructure, or
OSObjectPointer. All offsets are zero-relative. Accessing non-aligned addresses is
valid but might be slower on processors that do not directly support this style of
access.
address Answer the operating system memory address of the start of the data
element referenced by the receiver. This message is invalid for objects
whose data element is stored in Smalltalk memory.
bool16At: anInteger
Answer the 16-bit Boolean at byte offset anInteger from the start of the
storage area referenced by the receiver.
bool16At: anInteger put: aValue
Set the 16-bit Boolean at byte offset anInteger from the start of the storage
area referenced by the receiver to the integer value of aValue. aValue may
be nil, true, false, an Integer, a Character, or an OSObject. aValue is converted
using the same rules as the pointer call-out conversion. There is one
exceptioninstances are not allowed of OSObject that point to Smalltalk
memory that is not fixed.

Chapter 11. IBM Smalltalk Virtual Machine API 311


bool32At: anInteger
Answer the 32-bit Boolean at byte offset anInteger from the start of the
storage area referenced by the receiver.
bool32At: anInteger put: aValue
Set the 32-bit Boolean at byte offset anInteger from the start of the storage
area referenced by the receiver to the integer value of aValue. aValue may
be nil, true, false, an Integer, a Character, or an OSObject. aValue is converted
using the same rules as the pointer call-out conversion. There is one
exceptioninstances are not allowed of OSObject that point to Smalltalk
memory that is not fixed.
bool8At: anInteger
Answer the 8-bit Boolean at byte offset anInteger from the start of the
storage area referenced by the receiver.
bool8At: anInteger put: aValue
Set the 8-bit Boolean at byte offset anInteger from the start of the storage
area referenced by the receiver to the integer value of aValue. aValue might
be nil, true, false, an Integer, a Character, or an OSObject. aValue is converted
using the same rules as the pointer call-out conversion. There is one
exceptioninstances are not allowed of OSObject that point to Smalltalk
memory that is not fixed.
char16At: anInteger
Similar to bool16At:.
char16At anInteger put: aValue
Similar to bool16At:put:.
char8At: anInteger
Similar to bool8At:.
char8At anInteger put: aValue
Similar to bool8At:put:.
float32At: anInteger
Answer an instance of Float representing the 32-bit floating-point value at
byte offset anInteger from the start of the storage area referenced by the
receiver.
float32At: anInteger put: aValue
Set the 32-bit floating-point value at byte offset anInteger from the start of
the storage area referenced by the receiver to aValue. aValue must be an
instance of Float.
float64At: anInteger
Answer an instance of Float representing the 64-bit floating-point value at
byte offset anInteger from the start of the storage area referenced by the
receiver.
float64At: anInteger put: aValue
Set the 64-bit floating-point value at byte offset anInteger from the start of
the storage area referenced by the receiver to aValue. aValue must be an
instance of Float.
int16At: anInteger
Similar to bool16At:.
int16At anInteger put: aValue
Similar to bool16At:put:.

312 IBM Smalltalk: Programmers Reference


int32At: anInteger
Similar to bool32At:.
int32At anInteger put: aValue
Similar to bool32At:put:.
int64At: anInteger
Answer the 64-bit Integer at byte offset anInteger from the start of the
storage area referenced by the receiver.
int64At anInteger put: aValue
Set the 64-bit Integer at byte offset anInteger from the start of the storage
area referenced by the receiver to the integer value of aValue. aValue may
be nil, true, false, an Integer, a Character, or an OSObject. aValue is converted
using the same rules as the pointer call-out conversion. There is one
exceptioninstances are not allowed of OSObject that point to Smalltalk
memory that is not fixed.
int8At: anInteger
Similar to bool8At:.
int8At anInteger put: aValue
Similar to bool8At:put:.
pointerAt: anInteger type: anOSObjectSubclass
Create and answer a new struct instance of anOSObjectSubclass that points
to the storage of the receiver starting at anInteger. An error is signaled if
anInteger is less than anOSObjectSubclass fixedSize bytes from the end of the
receivers storage.
reference
Answer the Smalltalk memory referenced by the receiver. This message is
invalid for objects whose data element is stored in operating system
memory.
structAt: anInteger type: anOSObjectSubclass
Create and answer a new struct instance of anOSObjectSubclass and copies
enough bytes to fill it from the receiver starting at anInteger. An error is
signaled if anInteger is less than anOSObjectSubclass fixedSize bytes from the
end of the receivers storage.
structAt: anInteger put: anOSObject
Copy the storage referenced by anOSObject over the storage for the receiver
starting at anInteger. An error is signaled if anInteger is less than anOSObject
class fixedSize bytes from the end of the receivers storage.
memcpyFrom: start to: stop
Answer a ByteArray containing a copy of the bytes pointed to by the
receiver from byte offset start up to and including byte offset stop.
memcpyStringFrom: start to: stop
Answer a String containing a copy of the bytes pointed to by the receiver
from byte offset start up to and including byte offset stop.
memcpyFrom: start to: stop into: anOSOrBitObject startingAt: repStart
Copy bytes from byte offset start in the receiver up to and including byte
offset stop into anOSOrBitObject starting at byte index repStart. Answer
anOSOrBitObject. anOSOrBitObject might be a byte, word, or long object, or
an OSObject.
uint16At: anInteger
Similar to bool16At:.

Chapter 11. IBM Smalltalk Virtual Machine API 313


uint16At anInteger put: aValue
Similar to bool16At:put:.
uint32At: anInteger
Similar to bool32At:.
uint32At anInteger put: aValue
Similar to bool32At:put:.
uint64At: anInteger
Similar to int64At:.
uint64At anInteger put: aValue
Similar to int64At:put:.
int8At: anInteger
Similar to bool8At:.
int8At anInteger put: aValue
Similar to bool8At:put:.

Instance methods: freeing


free Free the storage for the receiver. If the receiver does not represent a pointer
to operating system memory, do nothing.

Instance methods: testing


= Answer a Boolean indicating whether the receiver and anOSObject are
equal. Two instances of OSObject are considered to be equal if they
represent the identical storage in operating system or Smalltalk memory.
memcmpFrom: start to: stop with: anOSOrBitObject startingAt: repStart
Compare the bytes in the receiver from offset start to stop with the bytes in
aCollection starting at repStart. If the bytes are all equal, answer 0. If the
bytes are not all equal, the result is based on the first bytes that are not
equal. If the byte from the receiver is less than the byte from aCollection,
answer -1. If the byte from the receiver is greater than the byte from
aCollection, answer 1. anOSOrBitObject might be a byte, word, or long
object, or an OSObject.
isImmediate
Answer true if the receiver is an immediate.
isAddress
Answer true if the receiver represents a pointer to operating system
memory.
isNull Answer true if the receiver is a NULL value; that is, if the receiver is a
pointer to location 0 in memory.
isAddress
Answer true if the receiver represents a pointer to Smalltalk memory.
notNull
Answer true if the receiver is not a NULL value.

Class methods: instance creation


address: anInteger
Answer a new instance of the receiver referring to operating system
memory at address anInteger.
calloc Answer a new instance of the receiver referring to storage in operating
system memory large enough to hold the fixedSize of the receiver in bytes.

314 IBM Smalltalk: Programmers Reference


alloc: anInteger
Answer a new instance of the receiver referring to storage in operating
system memory large enough to hold anInteger times the fixedSize of the
receiver in bytes.
new Answer a new instance of the receiver referring to Smalltalk storage large
enough to hold the fixedSize of the receiver in bytes.
reference: aByteArray
Answer a new instance of the receiver referring to the start of aByteArray.

OSStructure protocols
This section describes the protocols supported by OSStructure.

Instance methods: testing


equals: anOSObject
Answers a Boolean indicating whether the data element referenced by the
receiver and the data element referenced by anOSObject are equal. The
receiver and anObject must be of the same class, have the same indirection
level, and the data elements they reference must contain the same data, in
the same sequence by byte comparison.
indirectionLevel
Answer the receivers indirection level.

OSVariableStructure protocols
Class methods: initialization
variableSize
Answer an Integer that is the size in bytes of one data element of the
variable portion of the receiver.
variableSize: anInteger
Set an Integer that is the size in bytes of one data element of the variable
portion of the receiver. This value must be set before using an instance of
the receiver.

Class methods: instance creation


callocVariable: anInteger
Answer a new instance of the receiver referring to storage in operating
system memory large enough to hold the fixedSize of the receiver plus
anInteger times the variableSize of the receiver in bytes.
newVariable: anInteger
Answer a new instance of the receiver referring to storage in Smalltalk
memory large enough to hold the fixedSize of the receiver plus anInteger
times the variableSize of the receiver in bytes.

OSBaseType protocols
at: anInteger
Answer the data element at index anInteger from the data element
referenced by the receiver. Indexing is done in terms of data elements, not
bytes. Subclasses of OSBaseType override this method to return the
appropriate Smalltalk base type (for example, OSBool16 returns a boolean).
at: anInteger put: anOSObject
Set the data element at index anInteger in the data element referenced by

Chapter 11. IBM Smalltalk Virtual Machine API 315


the receiver. Indexing is done in terms of data elements, not bytes.
Subclasses of OSBaseType override this method to set the appropriate
Smalltalk base type (for example, OSBool16 sets a 16-bit boolean).
indirectionLevel
Answer the receivers indirection level.

ObjectPointer protocols
Class methods: initialization
defaultItemType
Answer the default item type for new instances of the receiver. When not
specified, instances of the receiver point to items of this class.
defaultItemType: anOSObjectSubclass
Set the default item type for new instances of the receiver. When not
specified, instances of the receiver point to items of this class.

Class methods: instance creation


address: addressInteger itemType: anOSObjectSubclass
Answer a new instance of the receiver with indirection level 2, initialized
to reference the operating system pointer to a data element described by
anOSObjectSubclass, at an address equal to addressInteger.
address: addressInteger itemType: anOSObjectSubclass indirectionLevel: indirectionLevel
Answer a new instance of the receiver with the specified indirection level,
initialized to reference the operating system pointer, at an address equal to
addressInteger.
calloc: anInteger itemType: anOSObjectSubclass
Allocate operating system memory large enough to contain anInteger
operating system pointers. Answer a new instance of the receiver with
indirection level 2, initialized to reference the operating system pointer to a
data element described by anOSObjectSubclass, in the allocated operating
system memory.
calloc: anInteger itemType: anOSObjectSubclass indirectionLevel: indirectionLevel
Allocate operating system memory large enough to contain anInteger
operating system pointers. Answer a new instance of the receiver with the
specified indirection level, initialized to reference the operating system
pointer, in the allocated operating system memory.
itemType: anOSObjectSubclass
Allocate Smalltalk memory large enough to contain one operating system
pointer. Answer a new instance of the receiver with indirection level 2,
initialized to reference the operating system pointer to a data element
described by the default item type, in the allocated operating system
memory.
itemType: anOSObjectSubclass indirectionLevel: indirectionLevel
Allocate Smalltalk memory large enough to contain one operating system
pointer. Answer a new instance of the receiver with the specified
indirection level, initialized to reference the operating system pointer to a
data element described by the default item type, in the allocated operating
system memory.
new: anInteger itemType: anOSObjectSubclass
Allocate Smalltalk memory large enough to contain anInteger operating
system pointers. Answer a new instance of the receiver with indirection

316 IBM Smalltalk: Programmers Reference


level 2, initialized to reference the operating system pointer to a data
element described by anOSObjectSubclass.
new: anInteger itemType: anOSObjectSubclass indirectionLevel: indirectionLevel
Allocate operating system memory large enough to contain anInteger
operating system pointers. Answer a new instance of the receiver with the
specified indirection level, initialized to reference the operating system
pointer to a data element described by anOSObjectSubclass.
reference: aByteObject itemType: anOSObjectSubclass
Answer a new instance of the receiver with indirection level 2, initialized
to reference aByteObject that contains operating system pointers to data
elements described by anOSObjectSubclass.
reference: addressInteger itemType: anOSObjectSubclass indirectionLevel: indirectionLevel
Answer a new instance of the receiver with the specified indirection level,
initialized to reference aByteObject that contains operating system pointers
to data elements described by anOSObjectSubclass.

Instance methods: accessing


at: anInteger
Answer the data element at index anInteger from the data element
referenced by the receiver. Indexing is done in terms of operating system
pointers, not bytes. If the receivers indirection level is 2, the value must be
an instance of the receivers itemType with indirection level 1. If the
receivers indirection level is greater than 2, the value must be an instance
of the receiver, with indirection one less than the receiver, and having the
same itemType.
at: anInteger put: anOSObject
Set the data element at index anInteger in the data element referenced by
the receiver. Indexing is done in terms of operating system pointers, not
bytes. If the receivers indirection level is 2, the value must be an instance
of the receivers itemType with indirection level 1. If the receivers
indirection level is greater than 2, the value must be an instance of the
receiver, with indirection one less than the receiver, and having the same
itemType.
indirectionLevel
Answer the receivers indirection level. The indirection level is defined as
the number of pointer dereferences required to reach the data element
described by the receiver.
itemType
Answer an instance of the receiver with indirection level 2, initialized to
reference aByteObject that contains operating system pointers to data
elements described by anOSObjectSubclass.

Methods available in other classes


The following instance methods are also available in ByteArray, String, and
DBString. They function exactly like the OSObject methods. All use zero-based
indexing.
int8At:
int8At:put:
int16At:
int16At:put:
int32At:
int32At:put:

Chapter 11. IBM Smalltalk Virtual Machine API 317


int64At:
int64At:put:
int8At:
uint8At:put:
uint16At:
uint16At:put:
uint32At:
uint32At:put:
uint64At:
uint64At:put:
memcpyFrom:to:
memcpyStringFrom:to:
memcpyfrom:to:into:startingAt:
memcmpFrom:to:with:startingAt:

User primitives
IBM Smalltalk supports three syntaxes for user primitives:
messagePattern
<primitive: primitiveName>
...fail code...
messagePattern
<primitive: 'sharedLibrary':functionName>
...fail code...
messagePattern
<primitive: 'sharedLibrary':functionNumber>
...fail code...

The first syntax looks up the primitive in the user primitive table. primitiveName is
an alphanumeric identifier. It is not surrounded by quotation marks. For example:
add: a and: b
<primitive: add2Numbers>
|self primitiveFailed

The other two syntaxes look up a user primitive function in a shared library, either
by name or by number. The sharedLibrary, functionName, and functionNumber fields
have the same syntax and function as those in a PlatformFunction. sharedLibrary
must be surrounded by quotes. functionName has no quotes. For example:
myFunc: arg1
<primitive: 'MYLIB':myFunc>
|self primitiveFailed

The user primitive acts the same way whether it is in the user primitive table or in
a shared library. If the primitive succeeds, the object returned from the primitive is
returned from the method. If the primitive fails, the fail code (the rest of the code
in the method) is run. If no fail code is provided, self is returned on primitive
failure. The suggested default fail code is |self primitiveFailed. It generates a
walkback describing the reason for the failure.

In C, a user primitive function is defined as:


EsUserPrimitive(functionName)
{
...your code...
}

Within the primitive, the following are defined:

318 IBM Smalltalk: Programmers Reference


EsPrimVMContext
Wherever a vmContext parameter is required when calling a function, pass
this value.
EsPrimReceiver
The receiver of the primitive method. It has type EsObject.
EsPrimArgumentCount
The number of arguments (not including the receiver) that were passed to
the primitive method.
EsPrimArgument(n)
The nth argument (an EsObject) of the primitive method. The value of n is
not validated.

If the primitive fails, use EsPrimFail(errorCode, argumentNumber). errorCode. is


one of the EsPrimErr values defined in Table 39 on page 336. argumentNumber is the
index of the argument in which the error occurred. If the error occurred in the
receiver, use EsPrimArgNumSelf. If the error occurred in no particular argument (for
example, an EsAllocateObject failed), use EsPrimArgNumNoArg. EsPrimFail returns
from the user primitive function.

If the primitive succeeds, use EsPrimSucceed(returnObject). returnObject must be


an EsObject. EsPrimSucceed returns from the user primitive function.

User primitive tables


Some IBM Smalltalk platforms require user primitives to be statically linked with
the virtual machine. Others require that all user primitives be placed in an external
shared library. See Platform requirements on page 333 for details about your
platform. In either case, user primitives are defined in the same way.

Platforms that require user primitives to be in an external shared library do not


have user primitive tables. All user primitive functions must be public (exported)
from the shared library. The name of the user primitive function must exactly
match the name used in the Smalltalk primitive declaration or PlatformFunction
function field.

For platforms that require static linking, all user primitives are accessed by the
virtual machine through the user primitive table, EsUserPrimitiveTable.

The file userprim.c contains an empty user primitive table. To add user primitives,
modify the file to include new primitives. For example:
EsDefinePrimitiveTable(EsUserPrimitiveTable)
EsPrimitiveTableEntry("nameFromSmalltalk", function)
...
EsEndPrimitiveTable

nameFromSmalltalk is the exact (case sensitive) name that the Smalltalk user
primitive used in the <primitive: > statement.

See the sections at the end of this chapter for platform-specific examples of
building a new user primitive table.

To allow multifile user primitives, tables can be nested, as follows:


/* userprim.c */
extern EsPrimitiveTable file1Table; /* prims from file1.c */
extern EsPrimitiveTable file2Table;
EsDefineUserPrimitive(function)

Chapter 11. IBM Smalltalk Virtual Machine API 319


{
...
}

EsDefinePrimitiveTable(EsUserPrimitiveTable)
EsPrimitiveTableEntry("nameFromSmalltalk", function)
EsSubTable(file1Table)
EsSubTable(file2Table)
...
EsEndPrimitiveTable

/* file1.c */

EsDefinePrimitiveTable(file1Table)
EsPrimitiveTableEntry("nameFromSmalltalk", function)
...
EsEndPrimitiveTable

Primitive tables have type EsPrimitiveTable.

Functions available in user primitives


This section defines the functions available in user primitives:
v Object allocation
v Sending messages
v Testing objects
v Converting Smalltalk objects to C values
v Converting C values to Smalltalk objects
v Accessing objects
v Protecting objects from garbage collection
v Miscellaneous

Object allocation
These functions enable users to allocate new objects from within a user primitive.
EsObject EsAllocateObject(EsVMContext vmContext, EsBehavior allocateClass,
U_32 size, U_32 saves, EsObject ** saveList)
Returns a new, initialized object of class allocateClass with size indexable
instance variables. Pass 0 for saves and NULL for saveList. The instance
variables of pointer objects are initialized to nil. All other objects are
initialized to 0.
Error cases: If the object could not be allocated for any reason, returns
NULL.
Smalltalk equivalent: allocateClass new: size
Side effects: This operation can cause a garbage collection.
EsObject EsAllocateFixedObject(EsVMContext vmContext, EsBehavior
allocateClass, U_32 size, U_32 saves, EsObject ** saveList)
Same as EsAllocateObject, except the object is allocated in fixed space.
This means that the object does not move during a garbage collect.
Error cases: If the object could not be allocated for any reason, returns
NULL.
Smalltalk equivalent: (allocateClass new: size) makeFixed

Sending messages
This function enables users to send a message from within a user primitive and
retrieve the result.

320 IBM Smalltalk: Programmers Reference


U_32 EsSendMessage(EsVMContext vmContext, EsObject * returnObject, EsObject
receiver, EsObject selector, U_32 argumentCount, ...)
Calls back into the IBM Smalltalk interpreter by sending a message.
returnObject is a pointer to an EsObject where the return value of the
message is placed. argumentCount can be from 0 to 255.
Error cases: Returns an EsPrimErr code.
Side effects: This operation can cause a garbage collection.

Testing objects
These functions enable users to determine what the class and type (immediate,
bytes, words, longs, or pointers) of an object are.
EsBehavior EsObjectClass(EsObject object)
Returns the class of object. The generic return type of EsBehavior is used
because the class of object can be a Class or Metaclass.
Smalltalk equivalent: object class
BOOLEAN EsIsImmediate(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is immediate.
BOOLEAN EsIsSmallInteger(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is a smallInteger.
BOOLEAN EsIsCharacter(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is a character.
BOOLEAN EsIsNil(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is nil.
BOOLEAN EsIsTrue(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is true.
BOOLEAN EsIsFalse(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is false.
BOOLEAN EsIsBoolean(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is true or false.
BOOLEAN EsIsLargeInteger(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is a largeInteger.
BOOLEAN EsIsFloat(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is a Float.
BOOLEAN EsIsBytes(EsObject object)
Returns TRUE or FALSE, indicating whether or not the instance variables of
object contain bytes.
BOOLEAN EsIsWords(EsObject object)
Returns TRUE or FALSE, indicating whether or not the instance variables of
object contain words.
BOOLEAN EsIsLongs(EsObject object)
Returns TRUE or FALSE, indicating whether or not the instance variables of
object contain longs.
BOOLEAN EsIsPointers(EsObject object)
Returns TRUE or FALSE, indicating whether or not the instance variables of
object contain pointers.

Chapter 11. IBM Smalltalk Virtual Machine API 321


BOOLEAN EsIsIndexable(EsObject object)
Returns TRUE or FALSE, indicating whether or not object has numbered
instance variables.
BOOLEAN EsIsReadOnly(EsObject object)
Returns TRUE or FALSE, indicating whether or not object is read-only.
BOOLEAN EsIsCharacters(EsObject object)
Returns TRUE or FALSE, indicating whether or not object contains instances
of Character. If this returns TRUE, object will be a byte or word object.

Converting Smalltalk objects to C values


These functions enable users to convert Smalltalk objects into C values for
processing in a user primitive.
I_32 EsSmallIntegerToI32(EsObject smallInteger)
Returns the signed 32-bit value of smallInteger.
Error cases: smallInteger is assumed to be a smallInteger.
char EsCharacterToChar(EsObject character)
Returns the char value of character.
Error cases: character is assumed to be a character. Some IBM Smalltalk
characters will not fit in a char. The return value in this case is unspecified.
U_16 EsCharacterToU16(EsObject character)
Returns the U_16 value of character.
Error cases: character is assumed to be a character.
U_32 EsIntegerToI32(EsObject object, I_32 * value)
Converts object to a C signed 32-bit Integer and stores the result in the
location pointed to by value. Returns an EsPrimErr code. If the return
value is EsPrimErrNoError then *value is valid. Otherwise, *value is
invalid.
Error cases: An error occurs if object is not a smallInteger or largeInteger.
An error occurs if the value of the number cannot fit in 32 bits signed.
U_32 EsIntegerToU32(EsObject object, U_32 * value)
Converts object to a C unsigned 32-bit integer and stores the result in the
location pointed to by value. Returns an EsPrimErr code. If the return
value is EsPrimErrNoError then *value is valid. Otherwise, *value is
invalid.
Error cases: An error occurs if object is not a smallInteger or largeInteger.
An error occurs if the value of the number cannot fit in 32 bits unsigned.
U_32 EsFloatToCDouble(EsObject object, double * value)
Converts object to a C double and stores the result in the location pointed
to by value. Returns an EsPrimErr code. If the return value is
EsPrimErrNoError then *value is valid. Otherwise, *value is invalid.
Error cases: An error occurs if object is not a Float.

Converting C values to Smalltalk objects


These functions enable users to convert C values into their corresponding objects
for returning to Smalltalk.
EsObject EsI32ToSmallInteger(I_32 value)
Converts value (a signed number) to an IBM Smalltalk smallInteger and
returns it.

322 IBM Smalltalk: Programmers Reference


Error cases: Not all signed 32-bit values can fit in a smallInteger. No range
check is done on value. Use EsI32ToInteger or EsU32ToInteger if you are
not certain that value fits.
EsObject EsCharToCharacter(char character)
Converts character to an IBM Smalltalk character and returns it.
EsObject EsU16ToCharacter(U_16 value)
Converts value to the corresponding IBM Smalltalk character and returns
it.
U_32 EsI32ToInteger(I_32 value, EsObject * object)
Converts value (a signed number) into an integer and stores the result in
the location pointed to by object. Returns an EsPrimErr code. If the return
value is EsPrimErrNoError then *object is valid. Otherwise, *object is
invalid.
Error cases: An error occurs if the new integer object cannot be allocated.
Side effects: This operation can cause a garbage collection.
U_32 EsU32ToInteger(U_32 value, EsObject * object)
Converts value (an unsigned number) into an integer and stores the result
in the location pointed to by object. Returns an EsPrimErr code. If the
return value is EsPrimErrNoError then *object is valid. Otherwise, *object
is invalid.
Error cases: An error occurs if the new integer object cannot be allocated.
Side effects: This operation can cause a garbage collection.
U_32 EsCDoubleToFloat(double value, EsObject * object)
Converts value into a Float and stores the result in the location pointed to
by object. Returns an EsPrimErr code. If the return value is
EsPrimErrNoError then *object is valid. Otherwise, *object is invalid.
Error cases: An error occurs if the new Float object cannot be allocated.
Side effects: This operation can cause a garbage collection.

Accessing objects
These functions enable users to access the instance variables of an object. For all of
the following functions that use an instance variable index, the first instance
variable is numbered 1.
U_32 EsInstSize(EsObject object)
Returns the number of named instance variables in object.
Smalltalk equivalent: object class instSize
U_32 EsBasicHash(EsObject object)
Returns the basic hash of the object.
Smalltalk equivalent: object basicHash
U_32 EsBasicSize(EsObject object)
Returns the number of indexed instance variables in object.
Smalltalk equivalent: object basicSize
U_32 * EsInstVarAddr(EsObject object)
Answers the address of the first instance variable of object. The result
value is cast to U_32 * regardless of the type of object.
Error cases: Assumes that object consists of not immediate.

Chapter 11. IBM Smalltalk Virtual Machine API 323


EsObject EsInstVarAt(EsObject object, U_32 index)
Returns instance variable index of object. Allows access of named and
indexed instance variables; that is, index 1 is the first named instance
variable, if object has any.
Error cases: Assumes that object consists of pointers. index is not
validated.
Smalltalk equivalent: object instVarAt: index
void EsInstVarAtPut(EsObject object, U_32 index, EsObject storedObject)
Stores storedObject into instance variable index of object. Allows access
of named and indexed instance variables; that is, index 1 is the first named
instance variable, if object has any.
Error cases: Assumes that object consists of pointers. index is not
validated. Stores into read-only objects are not checked.
Smalltalk equivalent: object instVarAt: index put: storedObject
EsObject EsAt(EsObject object, U_32 index)
Returns indexed instance variable index of object. Does not allow access
of named instance variables.
Error cases: Assumes that object consists of pointers and is indexable.
index is not validated.
Smalltalk equivalent: object basicAt: index
void EsAtPut(EsObject object, U_32 index, EsObject storedObject)
Stores storedObject into indexed instance variable index of object. Does
not allow access of named instance variables.
Error cases: Assumes that object consists of pointers and is indexable.
index is not validated. Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: storedObject
U_8 EsByteAt(EsObject object, U_32 index), U_8 EsU8At(EsObject object, U_32
index) Returns the unsigned byte in indexed instance variable index of object.
Error cases: Assumes that object consists of bytes. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsByteAtPut(EsObject object, U_32 index, U_8 value), void
EsU8AtPut(EsObject object, U_32 index, U_8 value)
Stores value into indexed instance variable index of object.
Error cases: Assumes that object consists of bytes. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value
U_16 EsWordAt(EsObject object, U_32 index), U_16 EsU16At(EsObject object,
U_32 index)
Returns the unsigned word in indexed instance variable index of object.
Error cases: Assumes that object consists of words. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsWordAtPut(EsObject object, U_32 index, U_16 value), void
EsU16AtPut(EsObject object, U_32 index, U_16 value)
Stores value into indexed instance variable index of object.

324 IBM Smalltalk: Programmers Reference


Error cases: Assumes that object consists of words. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value
U_32 EsLongAt(EsObject object, U_32 index), U_32 EsU32At(EsObject object,
U_32 index)
Returns the unsigned long in indexed instance variable index of object.
Error cases: Assumes that object consists of longs. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsLongAtPut(EsObject object, U_32 index, U_32 value), void
EsU32AtPut(EsObject object, U_32 index, U_32 value)
Stores value into indexed instance variable index of object.
Error cases: Assumes that object consists of longs. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value
I_8 EsSignedByteAt(EsObject object, U_32 index), I_8 EsI8At(EsObject
object, U_32 index)
Returns the signed byte in indexed instance variable index of object.
Error cases: Assumes that object consists of bytes. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsSignedByteAtPut(EsObject object, U_32 index, I_8 value), void
EsI8AtPut(EsObject object, U_32 index, I_8 value)
Stores value into indexed instance variable index of object.
Error cases: Assumes that object consists of bytes. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value
I_16 EsSignedWordAt(EsObject object, U_32 index), I_16 EsI16At(EsObject
object, U_32 index)
Returns the signed word in indexed instance variable index of object.
Error cases: Assumes that object consists of words. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsSignedWordAtPut(EsObject object, U_32 index, U_16 value), void
EsI16AtPut(EsObject object, U_32 index, U_16 value)
Stores value into indexed instance variable index of object.
Error cases: Assumes that object consists of words. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value
I_32 EsSignedLongAt(EsObject object, U_32 index), I_32 EsI32At(EsObject
object, U_32 index)
Returns the signed long in indexed instance variable index of object.
Error cases: Assumes that object consists of longs. index is not validated.
Smalltalk equivalent: object basicAt: index
void EsSignedLongAtPut(EsObject object, U_32 index, I_32 value), void
EsI32AtPut(EsObject object, U_32 index, I_32 value)
Stores value into indexed instance variable index of object.

Chapter 11. IBM Smalltalk Virtual Machine API 325


Error cases: Assumes that object consists of longs. index is not validated.
Stores into read-only objects are not checked.
Smalltalk equivalent: object basicAt: index put: value

Protecting objects from garbage collection


Whenever an object is allocated, a garbage collection might be required. If this
happens, all non-immediate object pointers stored in user primitive variables are
invalidated. If new objects are allocated in user primitives, they must be explicitly
protected from garbage collection using the EsSaveObject / EsRestoreObject
protocol.

Each object that is referenced only by the user primitive variables must be saved
before any operation that could cause a garbage collection. After that operation
completes, the objects must be restored in the reverse order from that in which they
were saved. All saved objects must be restored before the user primitive succeeds
or fails.
void EsSaveObject(EsObject object)
Pushes object onto the Smalltalk stack so that it is protected from a
garbage collection.
EsObject EsRestoreObject(void)
Pops the object on the top of the Smalltalk stack and returns it.

Miscellaneous
The virtual machine provides the following miscellaneous functions.
void EsRememberObjectStore(EsVMContext vmContext, EsObject targetObject,
EsObject storedObject)
Must be called whenever an object is stored into an instance variable of a
pointer object.
For example,
EsObject myObject;
EsObject storedObject;
EsObject * instVar;
instVar = (EsObject *) EsInstVarAddr(myObject);
*instVar = storedObject;
EsRememberObjectStore(EsPrimVMContext, myObject, storedObject);

Note: EsAtPut and EsInstVarAtPut automatically call this function.


U_32 EsVMVersion(void)
Returns the major and minor version numbers of the running virtual
machine. To extract the major and minor version numbers from the return
value:
U_32 versionNumber;
U_16 versionMajor;
U_16 versionMinor;

versionNumber = EsVMVersion();
versionMajor = versionNumber >> 16;
versionMinor = versionNumber & 0xFFFF;

The minor version number should be interpreted as a two-digit decimal


number. For example, version 1.21 of the interpreter has minor version 21.
Version 1.3 has minor version 30. Both have a major version of 1.
void EsScavenge(EsVMContext vmContext, U_32 bytesRequired)
Causes the scavenger (a part of the garbage collector) to run once.

326 IBM Smalltalk: Programmers Reference


Smalltalk equivalent: System scavenge
void EsGGC(EsVMContext vmContext, U_32 bytesRequired)
Causes the global garbage collector to run once.
Smalltalk equivalent: System globalGarbageCollect

Classes Available during User Primitives


The following classes (all of type EsBehavior) are available during a user primitive:
v EsPrimClassSmallInteger
v EsPrimClassTrue
v EsPrimClassFalse
v EsPrimClassUndefinedObject
v EsPrimClassCharacter
v EsPrimClassString
v EsPrimClassArray
v EsPrimClassByteArray
v EsPrimClassFloat
v EsPrimClassLargeInteger
v EsPrimClassDBString

Asynchronous messages (interrupts)


Interrupt handlers cannot send messages (using EsSendMessage) to the IBM
Smalltalk interpreter at arbitrary times. Interrupt-time messages must be queued
for later processing when the virtual machine has regained control of the system.
At certain checkpoints, the virtual machine polls the asynchronous message queue
and sends any pending messages.

Because an interrupt handler could become active at any time (for example, when
the virtual machine is garbage collecting), normal objects cannot be used in
asynchronous messages. To support this, IBM Smalltalk allows objects to become
fixed, meaning that they do not move during a garbage collection.

When posting an asynchronous message, the receiver, selector, and any arguments
must all be fixed objects. In Smalltalk, send makeFixed to all objects that will be
used from interrupt handlers. After all of the objects are fixed, install the interrupt
handler (for example, with a user primitive that takes the fixed receiver, selector,
and arguments as parameters).
void EsPostNMI(EsVMContext vmContext)
Causes nmi to be sent to the active process at the next checkpoint. The
message is sent even if the process has asynchronous messages disabled.
BOOLEAN EsPostAsyncMessage(EsVMContext vmContext, EsObject receiver,
EsObject selector, U_32 argumentCount, ...)
Queues a message for processing by the active process at the next
checkpoint. The message is not sent if the process has asynchronous
messages disabled. All of the objects passed here must be fixed.
Error cases: If the message could not be queued for any reason, returns
FALSE. TRUE indicates successful queuing.

When running an interrupt handler, EsPrimVMContext is not defined. During the


interrupt handler installation user primitive, you must perform the following steps:
ESGlobalInfo * gInfo; /* These are globals */
EsObject receiver;
EsObject selector;
...

Chapter 11. IBM Smalltalk Virtual Machine API 327


EsUserPrimitive(installInterruptHandler)
{
...save receiver/selector/args...
gInfo = EsPrimVMContext->globalInfo;
...install handler...
}

In the interrupt handler, pass gInfo->currentVMContext as the vmContext parameter


in EsPostAsyncMessage and EsPostNMI.

Do not save EsPrimVMContext in a global and use it in the handler. The saved
vmContext might not remain valid for the duration of the interrupt handler. The
globalInfo is always valid.

Using user primitive functions outside user primitives


Many of the functions described in this chapter can be used only from within a
user primitive, where EsPrimVMContext is defined. In order to use these functions
from other places, you must set up a dummy user primitive environment in your
function.

You first need to store a globalInfo. (See Asynchronous messages (interrupts) on


page 327 for details.) Then, you can handle the following code anywhere in the
local variable declaration section of your function:
ESGlobalInfo * gInfo;
myFunc()
{
...Variable declarations...
EsDefineUserPrimitiveEnvironment(gInfo);

...Now you can use any user primitive function...


}

You cannot use EsPrimReceiver, EsPrimArgumentCount, or EsPrimArgument(n) from


within the dummy environment. EsPrimVMContext is defined.

This mechanism can be used only from a clean point in the virtual machine. The
virtual machine is at a clean point when it calls a user primitive or
PlatformFunction. This mechanism should not be used during an interrupt handler,
because the virtual machine is not then at a clean point.

This mechanism is most useful for callbacks from the operating system. For
example, if you were to call out (using a PlatformFunction) to the operating system
and bring a window to the front, the operating system might immediately call
back asking the window to expose or paint. These messages can immediately be
sent to Smalltalk using EsSendMessage, because the virtual machine was at a clean
point when it called out to the operating system.

Sample user primitives for IBM Smalltalk


User primitives are stored in shared libraries. A shared library called sample exists
in the samples\primitiv directory. (For example, for OS/2 and Microsoft Windows,
the primitiv subdirectory contains sample.dll.) The name of the sample library must
be specified in the Smalltalk primitive declarations.

sample.c contains (at least) the following:

328 IBM Smalltalk: Programmers Reference


#include "esuser.h"
#include <string.h>

EsUserPrimitive(add2Numbers)
{
I_32 i1;
I_32 i2;
U_32 rc;
EsObject retVal;
rc = EsIntegerToI32(EsPrimArgument(1), &i1);
if (rc != EsPrimErrNoError)
EsPrimFail(rc, 1);
rc = EsIntegerToI32(EsPrimArgument(2), &i2);
if (rc != EsPrimErrNoError)
EsPrimFail(rc, 2);
rc = EsI32ToInteger(i1 + i2, &retval);
if (rc != EsPrimErrNoError)
EsPrimFail(rc, EsPrimArgNumNoArg);
EsPrimSucceed(retVal);
}
EsObject sampleCStringToString(EsPrimVMContext, str)
EsVMContext EsPrimVMContext;
char * str;
{
EsObject result;
U_32 i, size;
size = strlen(str);
result =
EsAllocateObject(
EsPrimVMContext,
EsPrimClassString,
size,
0, (EsObject **) NULL);
if (result != (EsObject) NULL)
for (i = 0; i < size; ++i)
EsByteAtPut(result, i + 1, (U_8) str[i]);
return result;
}
EsUserPrimitive(exampleMakeAString)
{
EsObject result;

result = sampleCStringToString(EsPrimVMContext,
"This is a C string");
if (result == (EsObject) NULL)
EsPrimFail(EsPrimErrNotEnoughMemory,
EsPrimArgNumNoArg);
EsPrimSucceed(result);
}
#ifdef LINKED_USER_PRIMITIVES

EsDefinePrimitiveTable(samplePrimitiveTable)
EsPrimitiveTableEntry("add2Numbers", add2Numbers)
EsPrimitiveTableEntry("exampleMakeAString",
exampleMakeAString)
EsEndPrimitiveTable

#endif

In Smalltalk, add the following methods to UndefinedObject:


add: a and: b
<primitive: 'sample':add2Numbers>
|self primitiveFailed

Chapter 11. IBM Smalltalk Virtual Machine API 329


makeAString
<primitive: 'sample':exampleMakeAString>
|self primitiveFailed

Now evaluate the following in a workspace (the expected result follows the --->):
nil add: 54 and: 7 ---> 61
nil add: 16r7FFFFFFF and: 1 ---> -2147483648 (overflow)
nil add: -1 and: -2 ---> -3
nil add: 1073741823 and: 1 ---> 1073741824 (LargeInteger)
nil add: $a and: 2 ---> walkback "invalid class in argument 1"
nil add: 0 and: 16r100000000 ---> walkback "value out of range in argument 2"
nil makeAString ---> 'This is a C string'

Sample callback for OS/2 and Microsoft Windows


The following example is the C code for the IBM Smalltalk window procedure for
OS/2 and Microsoft Windows. This example demonstrates how to call back into
Smalltalk. Similar code will also work under UNIX platforms.

Note: IBM Smalltalk supports external language entry points that can be used
instead of hand-written code. See Entry points on page 294.

Important points about the C code are:


v The call-in is not performed from a user primitive, so the
EsDefineUserPrimitiveEnvironment macro must be used before any IBM
Smalltalk API calls are made.
v The calls to EsSaveObject are necessary because the EsU32ToInteger call can
result in a garbage collection.
v The object result from EsSendMessage is a Smalltalk object, so it must be
converted back to C format before being returned.

Example callback code


/* Contents: IBM Smalltalk Window Proc interface for call-in.
Comments:
This file contains an implementation of the window proc
for Windows and OS/2. The window proc calls into Smalltalk to process
every event received. The default window proc is called
if an error occurred during processing of the call-in.
The most typical error is a walkback during call-in.
*/
#ifdef WIN32
/*
Includes different header file and redefines some result values
for Windows.
*/
#include <windows.h>
#ifndef MRESULT
#define MRESULT LRESULT
#endif
#ifndef MPARAM
#define MPARAM LPARAM
#endif
#ifndef EXPENTRY
#define EXPENTRY CALLBACK
#endif
#define WinDefWindowProc DefWindowProc
#endif
#ifdef OS2

330 IBM Smalltalk: Programmers Reference


/*
Includes different header file and redefines some result values
for OS/2.
*/
#define INCL_WININPUT
#include <os2.h>
#endif
#include "esuser.h"
typedef struct {
EsObject class;
U_32 flags;
U_32 size;
EsObject selector;
EsObject arguments;
EsObject receiver;
} ESDirectedMessage;
static BOOL RecursiveError = FALSE;
static BOOL PrimitiveError = FALSE;
static ESGlobalInfo *GlobalInfo = 0;
static ESDirectedMessage * WindowProcMessage = 0;
MRESULT EXPENTRY WindowProc (HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
MRESULT mresult;
EsDefineUserPrimitiveEnvironment(GlobalInfo);
EsObject result, objHwnd, objMsg, objMp1, objMp2;
extern int EsTerminated;
if (EsTerminated) {
return WinDefWindowProc (hwnd, msg, mp1, mp2);
}
if (RecursiveError) {
EsTTYOutputString ("\nRecursive WindProc Failure.");
EsTTYOutputString ("Using WinDefProc");
return WinDefWindowProc (hwnd, msg, mp1, mp2);
}
/*
Converts the arguments into Smalltalk objects
The parameters are converted into Smalltalk objects.
If the values are large enough, a LargeInteger may
be created. To prevent the garbage collection
from reclaiming any objects created, they are
saved on the Smalltalk stack for protection from the GC.
/*
PrimitiveError = FALSE;
if (EsU32ToInteger ((U_32) hwnd, &objHwnd) != EsPrimErrNoError) {
PrimitiveError = TRUE;
} else {
EsSaveObject(objHwnd);
if (EsU32ToInteger ((U_32) msg, &objMsg) != EsPrimErrNoError) {
EsRestoreObject();
PrimitiveError = TRUE;
} else {
EsSaveObject(objMsg);
if (EsU32ToInteger ((U_32) mp1, &objMp1) != EsPrimErrNoError) {
EsRestoreObject(); EsRestoreObject();
PrimitiveError = TRUE;
} else {
EsSaveObject(objMp1);
if (EsU32ToInteger ((U_32) mp2, &objMp2)
!= EsPrimErrNoError)
{
PrimitiveError = TRUE;
EsRestoreObject(); EsRestoreObject(); EsRestoreObject();

Chapter 11. IBM Smalltalk Virtual Machine API 331


} else {
objMp1 = EsRestoreObject();
objMsg = EsRestoreObject();
objHwnd = EsRestoreObject();
}
}
}
}
/* Sends the message to Smalltalk via call-in if there was no error */
if (PrimitiveError) {
EsTTYOutputString ("\nWindProc:EsU32ToInteger failed");
EsTTYOutputString ("Using WinDefProc");
RecursiveError = TRUE;
mresult = WinDefWindowProc (hwnd, msg, mp1, mp2);
RecursiveError = FALSE;
return mresult;
} else {
if (EsSendMessage(EsPrimVMContext,
&result,
WindowProcMessage->receiver,
WindowProcMessage->selector,
4,
objHwnd, objMsg, objMp1, objMp2) != 0) {
EsTTYOutputString ("\nWindProc:EsSendMessage failed.");
EsTTYOutputString ("Using WinDefProc");
RecursiveError = TRUE;
mresult = WinDefWindowProc (hwnd, msg, mp1, mp2);
RecursiveError = FALSE;
return mresult;
}
}
/* -2|31 to +2|31 */
if (EsIntegerToI32(result, &mresult) == EsPrimErrNoError) {
return mresult;
}
/* 0 to 2|32 */
if (EsIntegerToU32(result, &mresult) == EsPrimErrNoError) {
return mresult;
}
EsTTYOutputString ("\nWindProc Failed.");
EsTTYOutputString ("Result was not an Integer");
return WinDefWindowProc (hwnd, msg, mp1,mp2);
}
EsUserPrimitive(installWindowProc)
{
EsObject result;
GlobalInfo = EsPrimVMContext->globalInfo;
WindowProcMessage = (ESDirectedMessage *) EsPrimArgument(1);
if (EsU32ToInteger ((U_32) WindowProc, &result) != EsPrimErrNoError) {
EsPrimFail (EsPrimErrInvalidClass, 1);
}
EsPrimSucceed (result);
}
EsDefinePrimitiveTable(WindowProcPrimitiveTable)
EsPrimitiveTableEntry("installWindowProc", installWindowProc)
EsEndPrimitiveTable

Smalltalk code that uses the primitive above


The Smalltalk code that uses the primitive above is as follows:

332 IBM Smalltalk: Programmers Reference


The primitive declaration
installWindowProc: receiver selector: selector
"Private - Answer the address of the standard window proc.
Install the receiver and selector as the message that is
sent for every operating system message."
<primitive: installWindowProc>
|self primitiveFailed

Sample use of the primitive


initializeWindowClass
"Private - Get the standard IBM Smalltalk window procedure and
register the window class in the operating system."
| address |
"Get the address of the window proc and install the receiver and
selector. This message is sent from the window proc for every
operating system message."
address := self
installWindowProc: self makeFixed
selector: #windowProc:msg:with:with: makeFixed.
"Register the Smalltalk window procedure."
WindowClass := 'WINDOWPROC'.
Hab
winRegisterClass: WindowClass
pfnWndProc: address
flStyle: CsSizeredraw
cbWindowData: 4.

The Smalltalk code places the receiver and selector in fixed memory so that they
cannot move during garbage collection.

This code is based on the IBM Smalltalk code for the window procedure. Some
changes were made for clarity, so the code above might not match exactly the code
in IBM Smalltalk.

Platform requirements
This section describes, for each platform on which IBM Smalltalk runs, the
software required to write user primitives, the calling convention required for
PlatformFunction calls, and any other platform-specific information.

OS/2
Required software
The IBM VisualAge C++ compiler, or another compatible compiler, is required to
write user primitives.

User primitives
User primitives must be placed in a separate DLL. The DLL name must be
specified in the Smalltalk primitive declaration.

PlatformFunction calling convention


All functions called by way of the PlatformFunction mechanism must be declared
with the _System calling convention.

Chapter 11. IBM Smalltalk Virtual Machine API 333


Microsoft Windows 95, Windows 98, and Windows NT
Required software
The IBM VisualAge C++ compiler, or another compatible compiler, and the
Windows Software Development Kit are required to write user primitives. The user
primitive DLLs must be created under Windows 95, Windows 98, or Windows NT.

User primitives
User primitives must be placed in a separate DLL. The DLL name must be
specified in the Smalltalk primitive declaration.

PlatformFunction calling convention


All 32-bit functions called using the PlatformFunction mechanism must be
declared with the __stdcall calling convention.

Pascal16 and cdecl16 PlatformFunctions


IBM Smalltalk supports an extra PlatformFunction calling convention known as
pascal16 and cdecl16 on the OS/2 and Windows platforms. This convention allows
users to call functions in 16-bit DLLs. The creation and calling syntax is identical to
the 32-bit syntax, but the type conversions are performed differently.

16-bit PlatformFunctions convert and push char, bool, int8, int16, uint8, and uint16 as
16-bit values. pointer, object, struct, int32, and uint32 are converted and pushed as
32-bit values.

An example pascal16 function is:


add16: num16, and32: num32
<pascal16: int32 'MYDLL16.DLL':add16and32 int16 int32>
|self primitiveFailed

The equivalent C function would look like:


long FAR PASCAL add16and32(num16, num32)
{
return(num16 + num32);
}

An example cdecl16 function is:


add16: num16 and32: num32
<cdecl16: int32 'MYDLL16.DLL':_add16and32 int16 int32>
|self primitiveFailed

The C function would look like:


long FAR _cdecl add16and32(int num16, long num32)
{
return(num16 + num32);
}

All 16-bit functions must be declared FAR and exported from their DLL.
PlatformFunction objects can also be created using the pascal16 and cdecl16 calling
conventions.

Note: On OS/2, all FAR PASCAL functions are exported as uppercase. The IBM
Smalltalk code must use an uppercase name to refer to these functions.
_cdecl functions prepend an underscore to the name. The Smalltalk code
must specify the underscore when calling these functions.

334 IBM Smalltalk: Programmers Reference


The calling convention C16 is an alias for pascal16 and will be removed in future
releases.

Pascal16 and cdecl16 pointer conversion


IBM Smalltalk pointers are normally addressed using the linear (0:32) memory
model. Pascal16 and cdecl16 pointers use the selector:offset (16:16) model. When a
pointer, safePointer, object, or struct value is used as a parameter to a pascal16 or
cdecl16 function, it is automatically converted from 0:32 to 16:16. If a pointer,
safePointer, or object value is returned from a pascal16 or cdecl16 function, it is
automatically converted from 16:16 to 0:32. Immediate object values and the NULL
pointer (value 0) are not converted (that is, their value is the same as 0:32 or 16:16).

Two internal PlatformFunctions have been provided so that users can perform these
conversions manually, if required. They can be accessed as follows:
EsSelectorOffsetToLinear := "convert 16:16 to 0:32"
PlatformFunction
callingConvention: 'c'
function: 'EsSelectorOffsetToLinear'
library: nil
parameterTypes: #(uint32)
returnType: #uint32.
EsLinearToSelectorOffset := "convert 0:32 to 16:16"
PlatformFunction
callingConvention: 'c'
function: 'EsLinearToSelectorOffset'
library: nil
parameterTypes: #(uint32)
returnType: #uint32.

Note: If a pointer value is converted from 16:16 to 0:32 and then back to 16:16
(either automatically or manually), the final value will not necessarily be the
same as the original 16:16 value. It will, however, point to the same physical
memory location. Also, the functions do not assume that their argument is
an IBM Smalltalk object pointer, so they will erroneously convert immediate
object values.

These functions are also available from within user primitives. They are defined as
follows:
v U_32 EsSelectorOffsetToLinear(U_32 ptr)
v U_32 EsLinearToSelectorOffset(U_32 ptr)

They are available only on OS/2.

UNIX platforms
Required software
The C compiler supplied with the operating system is required to write user
primitives.

User primitives
User primitives are statically linked with the virtual machine for these platforms.
You can create shared libraries containing user primitives.

PlatformFunction calling convention


All functions called using the PlatformFunction mechanism must use the standard
Application Binary Interface (ABI) for the platform. Specialized calling
conventions, such as inline, are not supported.

Chapter 11. IBM Smalltalk Virtual Machine API 335


Solaris platforms
On Solaris platforms, you must use SPARCompiler 2.0 to compile user primitives.

Primitive error codes


All errors reported by IBM Smalltalk are found in the pool dictionary named
SystemPrimitiveErrors. To determine the cause of a particular error, use:
SystemPrimitiveErrors keyAtValue: errorNumber

In some cases, the error reported is 18 which is OSError. This indicates that an
OSError occurred during some primitive or system initialization. In the case of
image startUp, OSError 18 indicates that reading the image file resulted in an error.

Applications can return error codes from Smalltalk using the following code:
System exit: errorCode withObject: returnObject

The following error codes can be returned from the API functions or displayed in a
Fatal Application Error dialog box. Users can add new error codes with values
above EsPrimErrMinUserError.

Note: Not all of these error messages can actually appear in the Fatal Application
Error dialog.
Table 39. User Primitive Error Codes
Error Code Value Description
EsPrimErrNoError 0 No error occurred
EsPrimErrInvalidClass 1 An object did not have the correct type
(class)
EsPrimErrIndexOutOfRange 2 An index was out of range
EsPrimErrNotIndexable 3 An object that must be indexable was
not
EsPrimErrValueOutOfRange 4 A value was out of range
EsPrimErrReadOnly 5 An object was read-only and nothing
could be stored into it
EsPrimErrSignedNotDefined 6 Signed operations are not defined for
an object
EsPrimErrFPOverflow 7 Floating point overflow occurred in the
primitive
EsPrimErrFPDomainError 8 Floating point domain error
EsPrimErrFPGeneralError 9 Other floating point errors
EsPrimErrDivideByZero 10 Divide by zero attempted
EsPrimErrInvalidSize 11 An object did not have the correct size
EsPrimErrNotEnoughMemory 12 There was not enough memory to
allocate an object
EsPrimErrUnknownAPIType 15
EsPrimErrUnknownConversionType 16
EsPrimErrInvalidArgCount 17 Invalid argument count to the
primitive

336 IBM Smalltalk: Programmers Reference


Table 39. User Primitive Error Codes (continued)
Error Code Value Description
EsPrimErrOSError 18 An operating system error occurred
(for EsPrimErrOSError, return the
operating system error code in the
argumentNumber field of ESPrimFail)
EsPrimErrProcessDead 19 Invalid operation for a dead process
EsPrimErrProcessActive 20 Invalid operation for the active process
EsPrimErrUnboundFunction 22 Attempt to call an unbound
PlatformFunction
EsPrimErrUnimplementedPrimitive 23 Primitive is not yet implemented
EsPrimErrInvalidInCallback 24 Invalid operation in a callback into the
interpreter
EsPrimErrCannotReturn 25 Attempt to return twice from a method
EsPrimErrDebugNotSupported 26 No debug operations are supported
EsPrimErrDNURecursion 27 doesNotUnderstand: is not understood
EsPrimErrProcessTerminatedDuring- 28 Active process terminated during a
CallIn callback
EsPrimErrStackOverflow 29 The Smalltalk stack has overflowed
EsPrimErrInvalidReftype 30 An invalid OSObject reftype
EsPrimErrInvalidReference 31 An invalid OSObject reference
EsPrimErrInvalidOffset 32 An invalid OSObject offset
EsPrimErrImageFileOpenError 33 Could not open image file
EsPrimErrImageReadError 34 Read error occurred while loading
image
EsPrimErrMaxMemorySegments- 35 Maximum number of memory
Exceeded segments exceeded
EsPrimErrRSGrowFail 36 RememberedSet grow failed
EsPrimErrImageFileInvalid 37 Image file is not an image
EsPrimErrSupportDLLMissing 38 A required DLL cannot be located
EsPrimErrInternalError 39 An unspecified internal error has
occurred
EsPrimErrMaxImagesSavesExceeded 40 Maximum number of image saves
exceeded. You are running an
evaluation copy of IBM Smalltalk.
EsPrimErrAsyncQueueOverrun 41 The async message queue has
overflowed
EsPrimErrNMI 42 NMI has occurred
EsPrimErrUnimplementedByteCode 43 An unimplemented bytecode was
executed
EsPrimErrImageSaveError 44 An error occurred during image save
EsPrimErrInvalidAsyncMessage 45 An invalid async message was posted
EsPrimErrDecimalError 46 An error occurred in the decimal math
library
EsPrimErrDecimalPrecisionScale- 47 Precision must be greater than scale
Mismatch

Chapter 11. IBM Smalltalk Virtual Machine API 337


Table 39. User Primitive Error Codes (continued)
Error Code Value Description
EsPrimErrDecimalInvalidScale 48 Scale must be positive
EsPrimErrDecimalInvalidPrecision 49 Precision must be strictly positive
EsPrimErrDecimalPrecisionOverflow 50 Precision exceeds the maximum
EsPrimErrInvalidMemorySegment 51 Not valid memory segment specified
EsPrimErrGPF 52 General protection fault
EsPrimErrGPFInvalidRead 53 General protection fault-read from
invalid memory location
EsPrimErrGPFInvalidWrite 54 General protection fault-write to
invalid memory location
EsPrimErrGPFInvalidInstruction 55 General protection fault-invalid
instruction executed
EsPrimErrOutOfScopeReturnFrom- 56 Attempt to return out of scope over a
CallIn call in
EsPrimErrNoMoreEntryPoints 57 All available esEntryPoint addresses
are in use
EsPrimErrInvalidRelocation 58 Invalid image relocation
EsPrimErrFPUnderflow 59 Floating point underflow
EsPrimErrExitDueToWalkback 60 Exit due to walkback
EsPrimErrEvaluationTimeLimit- 61 Evaluation time limit has expired
Expired
EsPrimErrExitDueToStackDump 62 Exit due to error with stack dump
provided
EsPrimErrMinUserError 8192 Users can add new error codes above
this value

338 IBM Smalltalk: Programmers Reference


Chapter 12. Dynamic data exchange
Dynamic data exchange (DDE) is an interprocess communication mechanism
whereby two applications can exchange data in the style of a client/server
architecture.

This chapter:
v Introduces dynamic data exchange
v Explains how to build a DDE server and a DDE client

Introduction
A DDE conversation takes place between two applications. The client application
initiates a data exchange by requesting data from a server application. The server
responds by sending the requested data to the client application. A server can
provide data to many clients, but a client can make requests of only one server. An
application can create many DDE clients in order to exchange data with one or
more DDE servers. An application can be both a client and a server at the same
time. As in all client/server architectures, the client application always initiates the
DDE conversation.

The IBM Smalltalk implementation of DDE introduces DDE server and DDE client
objects. DDE servers can supply many DDE clients, but a DDE client can request
from only one server. An application written in IBM Smalltalk can have as many
DDE servers and clients as needed.

Previously, users have employed the clipboard for exchanging data between
different applications. This required the user to select the data, copy it to the
clipboard (usually by selecting a menu option), activate the second application, and
then paste the data into that application (again, usually by selecting a menu
option). This process is wholly dependent on the user, not only for copying the
data, but also for updating the data. DDE provides a mechanism for the complete
automation of this process. A DDE server application, for example, can be made to
send client data automatically without any user intervention.

There are many uses for this type of data exchange. A stock portfolio manager is
one example. Here a DDE stock server application might be created that, as part of
its initialization, would dial up a stock pricing service over a modem. It would
then collect data from the pricing service and make it available to any connected
DDE clients.

Upon startup, a DDE client application would make a request to connect to the
DDE stock server. The stock server would acknowledge that request and establish
a DDE conversation. The client application could then request continuous updates
on a specified, named piece of data (in this case the name of the stock). When the
data changed, the stock server application would automatically send a notification
to the client application. Then the DDE client application could recalculate its
entire portfolio and present its user with new or updated information.

DDE concepts and definitions


Before reading about building a DDE server or client, become familiar with the
following concepts and terms.

Copyright IBM Corp. 1994, 2000 339


DDE client
An application that acts as the receiver side of a DDE conversation with a
DDE server application. In IBM Smalltalk, the DdeClient class implements
the function and protocol to support the client side of a DDE conversation.
An instance of DdeClient can connect to a DDE server (any application,
Smalltalk or otherwise, that follows the particular platform DDE server
protocol) and then make data requests to that server.
DDE server
An application that acts as the producer side of a DDE conversation with a
DDE client application (any application, Smalltalk or otherwise, that
follows the particular platform DDE server protocol).
DDE event notification
Occurs in an application when a message arrives from the other side of a
DDE connection. For example, a DDE client application receives an event
notification when a particular piece of data in the DDE server application
changes, or a DDE server application receives an event notification
whenever a client application requests a connection.
Default database
Used by a DdeServerManager to hold all the items that the
DdeServerManager makes available to DDE clients through default
processing.
Default processing
Occurs when a server application enables the IBM Smalltalk DDE system
to handle all of the interaction with the DDE client. The server application
creates a database of topics along with the name of each data item within a
topic, the format specification of each data item, and the current value of
each data item. The server applications only role is to update the
appropriate database whenever a data item changes.
Callback
The primary means by which an application responds to DDE events.
Callbacks are procedures within an application that take some action in
response to events emanating from a DDE client or server. In IBM
Smalltalk, callbacks are methods in an application class that is using a
DdeClient or DdeServerManager to manage a DDE conversation. These
methods (that is, their selectors) along with the application object itself are
registered with the DDE objects, so that when a particular DDE event
occurs, the DDE object calls back to the application by sending the
specified method to the specified application object. If callbacks are not
registered with a DdeServerManager for one or more events, then
well-defined default processing will take place.
Application
In this chapter, the program that creates and uses the DdeServerManager, or
the DdeClient, or both. The word application is often used as one of the
three qualifiers for a piece of data. In DDE terms, a piece of data is
identified by an application, topic, and item. This chapter, however,
identifies data by server name, topic, and item. In IBM Smalltalk, the name
of a DDE server is simply a Smalltalk String passed as a parameter when
the DdeServerManager is created. Here is an example of the server name,

340 IBM Smalltalk: Programmers Reference


topic, and item hierarchy:

Application Topic Items Data


(Server name)

Stocks Time time (String) 10:25:36

time (Smalltalk Object) 10:25:36

Price BTRX (String) 25.60

EST (String) 104.56

Topic Identifies a logical grouping of data items within a named DDE server. A
piece of data is usually identified by an application (server name), topic,
and item. In a spreadsheet application, for example, the topic might be the
file name of a loaded spreadsheet.
Item Identifies a piece of data within a named topic of a named DDE server. An
items identification consists of a name and format specifier. The path to a
data item is usually denoted by a hierarchy of application (server name),
topic, and item.
Format
Specifies how a piece of data is to be interpreted by the receiving side of a
DDE conversation. In IBM Smalltalk, a format is specified by a Smalltalk
String (case insensitive). All data transferred to/from a DdeClient or a
DdeServerManager must be contained in a ByteArray object. The format
string tells each side of the DDE conversation how that data is to be
interpreted. There are certain predefined formats. The only exception to
this rule is the format String where the item being transferred must also
be a Smalltalk String.
System topics
A set of predefined topics and items that provide information of general
interest to DDE clients. Table 40 shows the predefined topics and items that
are supported by the default processing of the DdeServerManager.
Table 40. System topics
Topic Item Format Data
System Topics String A tab-delimited string of all the topics supported
by this server
System Formats String A tab-delimited string of all the formats for all
items
System SysItems String A tab-delimited string of all the items under the
System topic
System TopicItemList String A tab-delimited string of all the items under the
System topic
All topics TopicItemList String A tab-delimited string of all the items supported
by a topic
All topics Formats String A tab-delimited string of all the formats
supported by this topic

Hotlink
A request by a DDE client to a DDE server to be notified immediately

Chapter 12. Dynamic data exchange 341


when a particular data item changes, and to include with that notification
the value of the changed data item. That is, when a hotlinked data item
has changed, the DDE server generates an event notification back to the
client and sends the changed piece of data along with that event notice.
Warmlink
A request by a DDE client to a DDE server to be notified when a particular
data item changes. Unlike a hotlink, the DDE server only sends the event
back to the client and it does not send the changed data item along with
the notice. With a warmlink, the DDE client can explicitly request the
changed data from the server anytime after it has received notification of
the change.
Coldlink
A request by a DDE client to a DDE server to unlink an existing hotlink or
warmlink to a particular data item.

DDE classes
The following figure shows the typical Smalltalk DDE architecture. Either one of
these objects could be any other application that supports the DDE protocol for
either a DDE server or a DDE client.

Server Application Client Application

App Server App Client


Callback code Callback code

Callback App Client code


DdeClient
DdeServerManager DdeServer

DdeClient
DdeServer
message

DdeServer

The classes depicted in the figure are described in the following sections. Their
hierarchy is as follows:

Object
DdeClass
DdeClient
DdeServer
DdeServerManager

This hierarchy is not part of the windowing system (such as PM or Windows) but
it does use the windowing system of the underlying platform.

DdeClient class
An instance of the DdeClient class represents the client side of a DDE conversation.
Methods of DdeClient are available for connecting to a DDE server, requesting data
items from the server, requesting links to data items (hotlink, warmlink, or
coldlink), registering callbacks for data change and connection termination events,
sending data items to a server, querying server capabilities, and requesting
command execution by the server. A DdeClient can connect to any DDE server

342 IBM Smalltalk: Programmers Reference


application, Smalltalk or otherwise, that supports the platform DDE protocols. An
instance of DdeClient can connect to only one DDE server.

Note: Windows prevents a DDE client from seeing servers provided from the same
program (.exe).

DdeServer class
An instance of DdeServer represents a single connection to a single DDE client
application. Methods of DdeServer are available to send data to a client and to
disconnect from a DDE conversation. The DdeServerManager class described below
is the primary application interface for building DDE programs.

DdeServerManager class
Instances of DdeServerManager are used to manage multiple connections to DDE
client applications. Each connection maintained by a DdeServerManager is
represented by a DdeServer object. There are two primary techniques that
applications can employ in using a DdeServerManager. With the first technique an
application simply registers with the DdeServerManager the callbacks for one or
more of the DDE server events (see Table 41) and directly handles the processing
for those events. For example, an application can provide the code to handle a
hotlink request from a DDE client.

Alternatively, a second technique can be used that relies entirely on internal default
processing of the DdeServerManager and requires little involvement by the server
application. To utilize this approach an application describes to a DdeServerManager
a database of topics, along with the name of each data item within a topic, the
format specification of each data item, and the current value of each data item. The
DdeServerManager then handles all of the interaction with the DDE client. The only
role of the server application is to update the DdeServerManagers database
whenever a data item changes.

DdeServerManager has methods to add and remove DDE event callbacks, add and
remove database items, and update items in the database.

Note: If a callback has not been hooked, default processing occurs. What the
default processor does depends on the event that caused the callback and
items in the default database. If a callback is hooked and the returnValue
instance variable is set to nil, default processing still occurs.

DdeCallbackData class
An instance of DdeCallbackData is passed as a parameter to all DDE event callback
methods. It contains data about the current event and has a returnValue attribute
(set with returnValue:), which is used to return information back to the IBM
Smalltalk DDE subsystem.
Table 41. DdeCallbackData instance variables
Instance variable (class) Description
item (String) Name of the data item
format (String) Format for the data (see Formats of data transferred
between DDE servers and clients on page 362).
data (String or ByteArray) The actual data
application (String) The server name for this DDE conversation
topic (String) The topic name for this DDE conversation

Chapter 12. Dynamic data exchange 343


Table 41. DdeCallbackData instance variables (continued)
Instance variable (class) Description
reason (Symbol) Reason this callback is executing (contained in the Pool
Dictionary DdeConstants having the form DdeCR...)
returnValue (Boolean or nil) Value to be returned from the callback. See Table 46 on
page 364 for descriptions of return values and their
meanings.

DdeCallbackData has access methods to obtain the server name, the topic name, a
data item (name and possibly its value), and data item format specification. The
availability and significance of the information in a DdeCallbackData object are
event dependent. For a list of callbacks and return values for the
DdeServerManager, see Table 44 on page 363 and Table 46 on page 364. For a list of
callbacks for the DdeClient, see Table 45 on page 363.

Building a DDE server


There are two ways to build a DDE server: one has default processing for all client
requests, and the other enables your application to handle them. The following
descriptions use terms defined in the preceding section. An example follows each
description. See Discussion of the DdeServerManager on page 352 for further
information.

Throughout this chapter the code always compares in a case-insensitive manner.


Many different tests have been done with a wide variety of available software and
there is a wide variation among programs that preserve the case of strings and
those that do not. Therefore, it is always best to check strings in a case-insensitive
manner.

Building a DDE server that uses default processing


To build a DDE server application that uses default processing, do the following:
1. Create a DdeServerManager.
2. Build the default database by sending the addItem:topic:value:format: message to
the DdeServerManager for each item that you want to make available to DDE
clients.
3. Send the updateItem:topic:value:format: message to the DdeServerManager each
time the value of the item changes.
4. When the application is finished, send the free message to the
DdeServerManager.

By using default processing, the DdeServerManager (along with the default


database) handles all connection requests from DDE clients as well as requests for
hotlinks, warmlinks, coldlinks, and data.

By default, data sent from any DDE client to your DdeServerManager (a poke, in
DDE terminology) is rejected. Requests from the client for the server to run
commands are also rejected. However, both of these callbacks can be hooked,
enabling the application to service these requests.

An example DDE server that uses default processing


To build a DDE server application that uses default processing:

344 IBM Smalltalk: Programmers Reference


This example builds a DDE server that has the time and date in two different
formats for a total of four available items. The items are as follows:
v The current time in a String format
v The current time as a Smalltalk object
v The current date in a String format
v The current date as a Smalltalk object

First, create a class called DdeTimeServer that is a subclass of Object. Give it the two
instance variables described in Table 42.
Table 42. Instance variables for DdeTimeServer
Instance variable Description
ddeServerManager The instance of the DdeServerManager class that you are creating.
timerOn A flag indicating whether the timer is on or off. Turning the timer
off is important when the server shuts down.

Your new class looks like this:


Object subclass: #DdeTimeServer
instanceVariableNames: 'ddeServerManager timerOn '
classVariableNames: ''
poolDictionaries: ''

Now create a class method new to create a new server. Send the initialize method to
the new object in order to set it up.
new
"Answer a new server after initializing it."
|super new initialize

Before executing the new class method, you need to create the initialize method. In
the initialize method, first create the DdeServerManager and name it Timer. Next,
build the default database.

Note: With the default database setup you do not have to hook into any of the
callbacks provided by the DdeServerManager. All you need to do is to update
the items when the timer goes off. Notice that for the format of String, you
pass a Smalltalk String for the data. When the format is Smalltalk Object,
you use the IBM Smalltalk Swapper to convert the Smalltalk object into a
ByteArray. The method flatten to perform this function is specified in
Converting Smalltalk objects to a ByteArray and back on page 372.
initialize
"Private - Create and initialize a DdeServerManager. Set the name
of the server to 'Timer' and create two items in the item database
under the topic of 'Time'. The two items will be 'date' and 'time.'
Support two formats for the items, a string format and a Smalltalk
object. Support the Smalltalk object format by using the IBM
Smalltalk Swapper to turn a Smalltalk object into a ByteArray."

| currentDate currentTime |
ddeServerManager := DdeServerManager name: 'Timer'.
currentTime := Time now.
currentDate := Date today.
ddeServerManager
addItem: 'time'
topic: 'Time'
value: currentTime printString
format: 'String'.

Chapter 12. Dynamic data exchange 345


ddeServerManager
addItem: 'time'
topic: 'Time'
value: (self flatten: currentTime)
format: 'Smalltalk Object'.
ddeServerManager
addItem: 'date'
topic: 'Time'
value: currentDate printString
format: 'String'.
ddeServerManager
addItem: 'date'
topic: 'Time'
value: (self flatten: currentDate)
format: 'Smalltalk Object'.

self timerOn: true.

At the end of initialization the timer is turned on by sending the timerOn: message,
which in turn sends the timerSet message. The timer uses the Common Widgets
timer functionality so that when the timer goes off, the message timerProc: is sent
to the application.
timerOn: aBoolean
"Start/stop the timer depending on aBoolean."
timerOn := aBoolean.
self timerSet.

timerOn
"Answer whether the timer is on or off."
|timerOn.
timerSet
"If the timer is on, reset it to go off in one second."
self timerOn ifTrue: [
CwAppContext default
addTimeout: 1000
receiver: self
selector: #timerProc:
clientData: nil
]

Now all initialization has been completed. The main work of the server is now in
the method that is run whenever the timer goes off. When the timer goes off and
the timerProc: method is run, all the items in the default database are updated. As
the items are updated, the DdeServerManager looks for any hot- or warmlinks to the
data and updates the clients that are linked to those items.
timerProc: clientData
"The timer has gone off so save the current date and time and then
update the item database."

| currentDate currentTime |
currentTime := Time now.
currentDate := Date today.
ddeServerManager
updateItem: 'time'
topic: 'Time'
value: currentTime printString
format: 'String'.

ddeServerManager
updateItem: 'date'
topic: 'Time'
value: currentDate printString
format: 'String'.

346 IBM Smalltalk: Programmers Reference


ddeServerManager
updateItem: 'time'
topic: 'Time'
value: (self flatten: currentTime)
format: 'Smalltalk Object'.

ddeServerManager
updateItem: 'date'
topic: 'Time'
value: (self flatten: currentDate)
format: 'Smalltalk Object'.

self timerSet.

The final method that you need to implement is the free method. This method
stops the timer and frees up the DdeServerManager.
free
"Close down by stopping the timer and freeing up the
DdeServerManager."

self timerOn: false.


ddeServerManager free.

You now have a DDE server that not only provides the current time and date to
any DDE client but also allows access to the Systems Topics that should be
supported by all servers.

In this example server, the time and date are both updated when the timer goes
off. An improvement to the code might be to only update the date (and possibly
the time) if it has changed since the previous update.

Building a DDE server that does not use default processing


To build a DDE server application that does not use default processing:
1. Create a DdeServerManager.
2. Hook the necessary DdeServerManager callbacks (DdeNcoldlinkCallback,
DdeNhotlinkCallback, DdeNinitiateCallback, DdeNrequestCallback,
DdeNwarmlinkCallback).
3. Write the callback methods for each hooked callback.
4. When the application is finished, send the free message to the
DdeServerManager.

If you follow this procedure, the DdeServerManager will not handle any requests
from DDE clients. Methods must be written and callbacks hooked in order to
provide service. This technique of providing DDE server services takes more work
but allows for more flexibility.

Suppose, for example, that a user wants an application to make data available
through DDE but there is so much data, or the data is so large, that it must be
stored on secondary storage. When a DDE client requests data, the server must
read the data from secondary storage and return it to the client. To do this the
application must hook the DdeNrequestCallback callback and write a method to
handle requests for data from a DDE client.

Note: It is possible to have callbacks handled by a combination of default


processing and the DDE server application. If a callback is hooked, then the
application takes responsibility. If the callback is not hooked, then default
processing occurs.

Chapter 12. Dynamic data exchange 347


An example DDE server that does not use default processing
This example builds a DDE server that provides the time only in the String format.

Creating a timer: First, create a class called DdeTimeServer2 that is a subclass of


Object. It has the two instance variables described in Table 43.
Table 43. Instance variables for DdeTimeServer2
Instance variable Description
ddeServerManager The instance of the DdeServerManager class that you are creating.
timerOn A flag indicating whether the timer is on or off. Turning the timer
off is important when the server shuts down.

Thus, our new class looks like this:


Object subclass: #DdeTimeServer2
instanceVariableNames: 'ddeServerManager timerOn '
classVariableNames: ''
poolDictionaries: 'DdeConstants '

Second, create a class method new to create a new server. Also send the initialize
method to the new object in order to set it up.
new
"Create a new timer server and initialize it."
|super new initialize

Third, create the initialize method. In the initialize method you first create the
DdeServerManager and name it Timer. Next you hook up to the callbacks and turn
the timer on.
initialize
"Private - Create and initialize a DdeServer. Hook all callbacks."
ddeServerManager := DdeServerManager name: 'Timer'.
ddeServerManager
addCallback: DdeNcoldlinkCallback
receiver: self
selector: #coldlink:clientData:callData:
clientData: nil.

ddeServerManager
addCallback: DdeNhotlinkCallback
receiver: self
selector: #hotlink:clientData:callData:
clientData: nil.
ddeServerManager
addCallback: DdeNinitiateCallback
receiver: self
selector: #initiate:clientData:callData:
clientData: nil.

ddeServerManager
addCallback: DdeNrequestCallback
receiver: self
selector: #request:clientData:callData:
clientData: nil.
ddeServerManager
addCallback: DdeNwarmlinkCallback
receiver: self
selector: #warmlink:clientData:callData:
clientData: nil.

ddeServerManager
addCallback: DdeNterminationCallback

348 IBM Smalltalk: Programmers Reference


receiver: self
selector: #termination:clientData:callData:
clientData: nil.
self timerOn: true.

At the end of initialization, turn the timer on by sending the timerOn: message,
which in turn sends the timerSet message. These methods are implemented below.
The timer uses the Common Widgets timer functionality so that when the timer
goes off, the message timerProc: is sent to the application.
timerOn: aBoolean
"Start/stop the timer depending on aBoolean."
timerOn := aBoolean.
self timerSet.

timerOn
"Answer whether the timer is on or off."
|timerOn.
timerSet
"If the timer is on, reset it to go off in one second."
self timerOn ifTrue: [
CwAppContext default
addTimeout: 1000
receiver: self
selector: #timerProc:
clientData: nil
]

Implementing server behavior: Next, create the callback methods that implement
the behavior of this server. First you implement the callback for the
DdeNinitiateCallback. The initiate:clientData:callData: method checks that the
application name and the topic to which the client is attempting to connect match
what the server is looking for. If they do match, then the application sends the
notifyClientOfSupportFor: message to the DdeServer and sets the return value of the
DdeCallbackData object to true. To reject the connect attempt, set the return value of
the DdeCallbackData object to false. Notice that you are not attempting to handle an
empty string for the application name or the topic name. The code should be
embellished to handle this possible situation.
initiate: aDdeServer clientData: unused callData: ddeCallbackData
"Initiate callback - check the application name and topic
and set the returnValue to true if they match."
| app top |
app := ddeCallbackData application asLowercase.
top := ddeCallbackData topic asLowercase.

((app = 'timer') and: [top = 'time'])


ifTrue: [
self message:(
'Connect. App = %1 Topic = %2'
bindWith: app with: top).
aDdeServerManager notifyClientOfSupportFor: 'Time'.
ddeCallbackData returnValue: true.
]
ifFalse: [
self message: (
'Reject. App = %1 Topic = %2'
bindWith: app with: top).
ddeCallbackData returnValue: false.
]

Handling links: The code that follows is for the DdeNhotlinkCallback method. The
method checks the item name and the format. It sets the DdeCallbackData object to
true if the item is supported and false otherwise.

Chapter 12. Dynamic data exchange 349


hotlink: ddeServer clientData: unused callData: ddeCallbackData
"Hotlink callback - Check the item name and the format.
Set the returnValue to true if the item is 'time' and
the format is 'String', false otherwise."

| item format string |


item := ddeCallbackData item asLowercase.
format := ddeCallbackData format asLowercase.
string := 'Item = %1 Format = %2' bindWith: item with: format.
(item = 'time' and: [format = 'string'])
ifTrue: [
self message: ('Hotlink. %1' bindWith: string).
ddeCallbackData returnValue: true.
]
ifFalse: [
self message: ('Hotlink rejected. %1' bindWith: string).
ddeCallbackData returnValue: false.
]

The method for handling the warmlink and coldlink has the same form as the
method for the hotlink. Instead of implementing the code shown below, the
method could test the reason value of the DdeCallbackData object (by way of the
reason message) and print out different messages based on the value that is
returned.
warmlink: ddeServer clientData: unused callData: ddeCallbackData
"Warmlink callback - Check the item name and the format.
Set the returnValue to true if the item is 'time' and
the format is 'String'."
| item format string |

item := ddeCallbackData item asLowercase.


format := ddeCallbackData format asLowercase.
string := 'Item = %1 Format = %2' bindWith: item with: format.
(item = 'time' and: [format = 'string'])
ifTrue: [
self message: 'Warm link. %1' bindWith: string.
ddeCallbackData returnValue: false
]
ifFalse: [
self message: 'Warm link rejected. %1' bindWith: string.
ddeCallbackData returnValue: true.
]

Note: The Excel spreadsheet program sends coldlink messages with an empty
string as the format.
coldlink: ddeServer clientData: unused callData: ddeCallbackData
"Coldlink callback - Check the item name."
| item format string |

item := ddeCallbackData item asLowercase.


format := ddeCallbackData format asLowercase.
string := 'Item = %1 Format = %2' bindWith: item with: format.
item = 'time'
ifTrue: [
self message: 'Cold link. %1' bindWith: string.
ddeCallbackData returnValue: true.
]
ifFalse: [
self message: ('Cold link rejected. %1' bindWith: string).
ddeCallbackData returnValue: false.
]

350 IBM Smalltalk: Programmers Reference


Handling client requests: The next callback you implement is
DdeNrequestCallback. This callback is run when the client requests data from the
server. As in previous examples, you check whether the item and the format are
what you are looking for, then send the data to the client by sending the message
sendItem:value:format: to the DdeServer that was passed to the callback method. The
return value of the DdeCallbackData object should be set to true if the data is sent to
the client and false if the data is not sent to the client.
request: ddeServer clientData: unused callData: ddeCallbackData
"Request callback - Check the item name and the format. If they
match what you are looking for then send the data to the client."

| item format string |

item := ddeCallbackData item asLowercase.


format := ddeCallbackData format asLowercase.
string := 'Item = %1 Format = %2' bindWith: item with: format.
(item = 'time' and: [format = 'string'])
ifTrue: [
self message: 'Request. %1' bindWith: string.
ddeServer
sendItem: ddeCallbackData item
value: Time now printString
format: 'String'.
ddeCallbackData returnValue: true.
]
ifFalse: [
self message: ('Request rejected. %1' bindWith: string).
ddeCallbackData returnValue: false.
]

Ending: The last callback you implement is the DdeNterminationCallback. This


method should not attempt to disconnect. It only prints to the Transcript that the
server dropped a connection.
termination: ddeServer clientData: unused callData: ddeCallbackData
"Termination callback - Print out a message that the server is
disconnecting."

self message: 'Connection terminated'.

Now all initialization has been completed and all the callbacks have been set up.
The main work of the server is now contained in the method that is run whenever
the timer goes off. When the timer goes off and the timerProc: method is run, the
one supported item is updated. As the item is updated the DdeServerManager looks
for any hot- or warmlinks to the data and updates the clients that are linked to
that piece of data.
timerProc: clientData
"The timer has gone off so update the item database."

ddeServerManager
updateItem: 'time'
topic: 'Time'
value: Time now printString
format: 'String'.

self timerSet.

Note: After the updateItem:topic:value:format: message is completed, the data passed


as the value parameter is lost. The DdeServerManager does not keep a copy
of this data. When the default database is used, the data is kept so that
when a client requests the data the DdeServerManager can give the data back
without intervention from the application. The implication is that if a server

Chapter 12. Dynamic data exchange 351


has many items or lots of data, it might not be able to use default processing
and the default database because of memory restrictions. In that case the
server has to hook the callbacks and process the events itself.

The message: method that has been used throughout the example follows:
message: aString
"Print a message on the Transcript."

Transcript cr; show: 'Timer Server: ', aString

The final method that you need to implement is the free method. This method
stops the timer and frees up the DdeServerManager.
free
"Close down by stopping the timer and closing down the DdeServer."

self timerOn: false.


ddeServerManager notNil ifTrue: [ddeServerManager free]

You now have a DDE server that provides the current time in String format.

An improvement to the code might be to support the empty application name


(server name) and an empty topic name as well as both the date and time in other
formats.

Discussion of the DdeServerManager


To act as a DDE server, an application creates a DdeServerManager object using the
class method name:, which takes a string argument as the name of the new
DdeServerManager. This is the name for DDE clients to use in establishing a
connection with the server. The application can then register for any of the
callbacks listed above for the DdeServerManager. When a DDE client connects to the
server, the DdeServerManager creates a new DdeServer object to handle the
conversation with that client. Hence, an application only creates one instance of
DdeServerManager, but an instance of DdeServer is created for each conversation
that the DdeServerManager is managing. The following figure illustrates this setup
of runtime DDE objects and applications.

Note: Applications should never directly create DdeServer objects.

Application

DdeClient
DdeServer

DdeServerManager DdeServer Application

DdeServer DdeClient

Application
Application

DdeClient

352 IBM Smalltalk: Programmers Reference


Default database
To offload DDE server event processing from the developers application to the
DdeServerManager, you can create a database of topics and data items for the
DdeServerManager, where the data items are stored with their name, format, and
current value. Items can be added to the database with the following message to a
DdeServerManager:
aDdeServerManager
addItem: itemName
topic: topicName
value: data
format: formatName

Building a database of all topics and data items lets the DdeServerManager perform
its default processing in servicing requests from DDE clients, without requiring any
intervention from the application. To illustrate, the following code creates a
DdeServerManager and builds a database of topics and items that will be serviced.
| aDdeServerManager |
aDdeServerManager := DdeServerManager name: 'Stocks'.
aDdeServerManager
addItem: 'time'
topic: 'Time'
value: Time now printString
format: 'String';
addItem: 'time'
topic: 'Time'
value: (self flatten: Time now)
format: 'Smalltalk Object';
addItem: 'BTRX'
topic: 'Price'
value: 26.50 printString
format: 'String';

addItem: 'EST'
topic: 'Price'
value: 104.56 printString
format: 'String'

The following figure shows an example DdeServerManager default database after


executing the previous code.
Application Topic Items Data
(Server name)

Stocks Time time (String) 10:25:36

time (Smalltalk Object) 10:25:36

Price BTRX (String) 25.60

EST (String) 104.56

The IBM Smalltalk Swapper makes it straightforward to use DDE to pass Smalltalk
objects back and forth between cooperating Smalltalk applications. The method
flatten: in the above code converts a Smalltalk object into a ByteArray. The Swapper
can also be used on the receiving side of a DDE connection to unflatten the
Smalltalk object. The code for doing this is specified in Converting Smalltalk
objects to a ByteArray and back on page 372.

Chapter 12. Dynamic data exchange 353


The default processing database for an instance of DdeServerManager contains a
single server name, one or more topics that are associated with that server name,
and one or more items for each topic. An application should only use one instance
of a DdeServerManager.

Once a DdeServerManagers database is constructed, the application need only


update an item in the database when the data associated with that item changes.
The DdeServerManager then automatically takes the appropriate action with any
DDE clients linked to that item. For example, if the price of the EST stock changes,
then the application runs the following code:
ddeServerManager
updateItem: 'EST'
topic: 'Price'
value: 100.01 printString
format: 'String'

In response to this message, the DdeServerManager automatically updates any


clients that have links or outstanding requests for that data item (assuming that the
application does not override the DdeNrequestCallback). In addition, the
DdeServerManager can automatically respond to DDE client requests for any of the
predefined general-interest topics and items listed in Table 40 on page 341.

Initiate event
When a DDE client attempts to connect to a DDE server, the DdeNinitiateCallback
runs on the server application (assuming the DdeNinitiateCallback is hooked). The
DdeCallbackData object passed to the callback contains the topic strings and the
name of the server to which the client is trying to connect. If the application wants
to accept the connect request, then it must send the following message:
ddeServerManager notifyClientOfSupportFor: topicStr

topicStr is the server application topic string. Note that it is possible for the client
to send an empty string for the topic request. In this case, the server application
should send the message notifyClientOfSupportFor: for each topic that it supports. It
is also possible for the DDE client to use an empty string for the server name. This
implies that any server, regardless of name, can respond to the connect request. For
example, a server application might implement the DdeNinitiateCallback callback as
follows:
initiate: aDdeServerManager
clientData: clientData
callData: callData
"If the application and the topic are empty, then notify the
client about both of the topics."

callData application isEmpty


& callData topic isEmpty ifTrue: [
|aDdeServerManager
notifyClientOfSupportFor: 'Topic1';
notifyClientOfSupportFor: 'Topic2'.
].
"If the application does not match the server name then
reject the connect request."
callData application asLowercase = 'server' ifFalse: [
|self
].
"At this point the application name matches our server name.
Check for a connect request to a topic you support."
callData topic = 'Topic1' ifTrue: [
|aDdeServerManager
notifyClientOfSupportFor: 'Topic1'.

354 IBM Smalltalk: Programmers Reference


].
callData topic = 'Topic2' ifTrue: [
|aDdeServerManager
notifyClientOfSupportFor: 'Topic2'.
].
"There is no topic that you support so reject the connect request."

Note: The return value was not used. See Table 46 on page 364.

If DdeServerManager default processing is being used for the initiate request (that is,
the DdeNinitiateCallback is not hooked), then the DdeServerManager checks the
server name and the topic names in the default processing database and, if found,
creates a connection for the requested topic. However, if the DdeServerManager
receives an empty string for the server name or topic name, it responds by sending
notifyClientOfSupportFor: for each topic in the default processing database. Once
connected, a DDE client can request only items associated with the topic specified
in the connect request. A client must disconnect and then reconnect to access items
in a different topic. The following figure shows how a DDE client initiates a
connect request to a DDE server, thereby generating the initiate event.

Server application

DdeServerManager

Server 2.DdeNinitiateCallback
Application
Callback
Code 3.notifyClientOfSupportFor:

1.connect 4.connect
acknowledgement

Client Application
Dde client

Warmlink and hotlink event


After the DDE conversation has been established, a DDE client might try to link to
a particular data item. When a client attempts a link to a data item, the
DdeNwarmlinkCallback or DdeNhotlinkCallback is run on the server application
(assuming DdeNwarmlinkCallback or DdeNhotlinkCallback are hooked). In this case,
the DdeCallbackData object passed to the callback contains the name of the item, its
format, and the current application and topic names. For example, to register for
the DdeNwarmlinkCallback, a DDE server application uses:
ddeServerManager
addCallback: DdeNwarmLinkCallback
receiver: self
selector: #warmLink:clientData:callData:
clientData: nil

Chapter 12. Dynamic data exchange 355


The callback warmLink:clientData:callData: method looks like this:
warmLink: aDdeServer clientData: clientData callData: callData
"A DDE client has requested a warm link to a piece of data. Check
the name and format and if supported set the return value to true
to acknowledge the connect request. Otherwise set the return value
to false."

| item format |
item := callData item asLowercase.
format := callData format asLowercase.
(item = 'time' and: [format = 'string'])
ifFalse: [callData returnValue: false]
ifTrue: [
Transcript show: 'warmLink ', item, ' ', format.
callData returnValue: true.
]

By setting the return value of the DdeCallbackData object to true, the DDE server
application has indicated that the item/format is supported. In that case, the
DdeServerManager creates a link to that item for the client, and it generates the
necessary events back to the DDE client when the data item is updated. The server
application must set the return value of the DdeCallbackData object to true if the
item/format is supported and to false if the item/format is not supported. A nil
return value in the DdeCallbackData object causes the DdeServerManager to look up
the item in its default database and, if found, link the client to that item.

Coldlink event
When a DDE client unlinks from a data item, the DdeNcoldlinkCallback is run on the
DDE server application (assuming the DdeNcoldlinkCallback is hooked). As with
hot- and warmlinking, the DdeCallbackData object passed to the callback contains
the name of the item to be unlinked, its format, and the current application and
topic names.

If the DDE server application sets the return value of the DdeCallbackData object to
true, then the DdeServerManager checks for an existing link to the data item and, if
one exists, it unlinks the client from that item. In this case, if a link to the data
item does not exist, the DdeServerManager generates an error response back to the
client. On the other hand, a false return value in the DdeCallbackData object has no
effect on the linkage status of the specified data item.

The DdeNcoldlinkCallback is unique in that a nil return value in the DdeCallbackData


object is the same as a true return value.

Request event
When a client wants to retrieve data from a server, the DdeNrequestCallback is run
on the server application (assuming the DdeNrequestCallback is hooked). Here the
DdeCallbackData object passed to the callback contains the name of the item, its
format, and the current application and topic names. If the data is available, the
application sets the return value of the DdeCallbackData object to true and sends the
message sendItem:value:format: to the DdeServer object passed as the first parameter
of the callback. For example, to register for the DdeNrequestCallback, a DDE server
application uses:
ddeServerManager
addCallback: DdeNrequestCallback
receiver: self
selector: #request:clientData:callData:
clientData: nil

The callback request:clientData:callData: method looks like:

356 IBM Smalltalk: Programmers Reference


request: aDdeServer clientData: clientData
"A DDE client is requesting a piece of data. Check that the item and
topic and item are supported. If the item/format is not supported,
set the return value to false. If the item/format is supported then
send the data to the client and set the return value to true."

| item format |
item := ddeCallbackData item asLowercase.
format := ddeCallbackData format asLowercase.
(item = 'time' and: [format = 'string'])
ifFalse: [ddeCallbackData returnValue: false]
ifTrue: [
aDdeServer
sendData: ddeCallbackData item
format: 'String'
with: Time now printString.
ddeCallbackData returnValue: true.
]

If nil is set for the return value of the DdeCallbackData object (or if the
DdeNrequestCallback is not hooked), the DdeServerManager performs its default
processing, which in this case is to check for the item in the default database and if
found, send it back to the client.

Run and poke events


When a DDE client wants to send data (called poke in DDE parlance) to a
server, the DdeNpokeCallback is run on the server application (assuming the
DdeNpokeCallback is hooked). The DdeCallbackData object passed to the callback
method contains the name of the data item, its format, and the current application
and topic names. Server handling of this event is similar to the hot/warm/cold
link events.

When a DDE client makes a command execution request on a server, the


DdeNexecuteCallback is invoked on the server (again assuming the
DdeNexecuteCallback is hooked). Here the DdeCallbackData object passed to the
callback method contains the command to be run (as a string), and the current
application and topic names. The command string can be accessed from the
DdeCallbackData object through the execute message.

Termination event
When a DDE client shuts down or sends a connection termination request to a
server, the DdeNterminationCallback method is run on the server application. The
server application should not try to disconnect at this point because the client
cannot be stopped from terminating the connection. This callback only exists to
notify the server that the connection is terminating. Also, the server application
should not try to free the DdeServer passed as the first parameter of the callback.
The DdeServerManager manages the DdeServer objects for each connection and takes
appropriate action as required.

Building a DDE client


The following description uses terms defined in DDE concepts and definitions on
page 339. An example follows the description. See Discussion of the DdeClient
on page 359 for further information.

To build a DDE client application:


1. Create a DdeClient.

Chapter 12. Dynamic data exchange 357


2. Connect to a DDE server by sending the message connect:topic: to the DdeClient
with the name of the server and the name of the topic that you want to connect
to.
3. After the connection is made, messages can be sent to the DdeClient to request
data, link to data (hotlink, warmlink, or coldlink), send data to the server, or
ask the server to run a command.
4. To handle the arrival of data asynchronously from the server (that is, hotlinked
and warmlinked items), the application must hook the DdeNdataCallback or the
DdeNchangeCallback callbacks.
5. To connect to a different server or topic, send the disconnect message to the
DdeClient and then send the connect:topic: message with the name of the server
and the name of the topic to connect to.
6. When the application is finished, send the free message to the DdeClient.

An example DDE client


In this example, a DDE client exchanges data with the example DDE server created
previously. This client connects to the server, sets up a hotlink to the time and
date, and prints it in the Transcript window when it changes.

First, create a class called DdeTimeClient that is a subclass of Object. It will have the
ddeClient instance variable that represents the instance of the DdeClient class that
you will create.

Thus, your new class looks like:


Object subclass: #DdeTimeClient
instanceVariableNames: 'ddeClient '
classVariableNames: ''
poolDictionaries: 'DdeConstants '

Note: Include the pool dictionary DdeConstants because you are going to hook into
a callback that needs this pool dictionary.

Now, add a class method new to create a new client. Also send the initialize method
to the new object in order to set it up.
new
"Create a new timer client and initialize it."
|super new initialize

Now you create the initialize method. This method creates the DdeClient object and
connects it to the server. Next it hooks up to the hotlink callback so that when the
data at the server changes, the client is notified. Then the application attempts to
hotlink to the time and date items at the server.
initialize
"Initialize the dde client."
ddeClient := DdeClient new.
(ddeClient connect: 'Timer' topic: 'Time') ifFalse: [
|Transcript cr; show: 'Client cannot connect'.
].
ddeClient
addCallback: DdeNdataCallback
receiver: self
selector: #data:clientData:callData:
clientData: nil.

358 IBM Smalltalk: Programmers Reference


(ddeClient hotLink: 'time' format: 'Smalltalk Object')
ifFalse:[
Transcript
cr;
show:'Cannot hotlink to time'
].
(ddeClient hotLink: 'date' format: 'Smalltalk Object')
ifFalse:[
Transcript
cr;
show:'Cannot hotlink to time'
].

Next, create the hotlink callback method. All callbacks take three parameters: the
DDE object calling the method, the client data passed when the callback was
hooked, and the DdeCallbackData object containing the information about the
callback.

When the callback is run the method unflattens the data in the DdeCallbackData
object from a ByteArray into a Smalltalk object (because this is what we hotlinked
to in the first place). The unflatten: method takes a ByteArray and uses the IBM
Smalltalk Swapper to convert it back into a Smalltalk object. The code for doing
this is specified in Converting Smalltalk objects to a ByteArray and back on
page 372.

After converting the data back to a Smalltalk object, the method then prints the
object in the Transcript window.
data: aDdeClient clientData: clientData callData: callData
"Hotlink callback - unflatten the data and display it."

| data |
data := self unflatten: callData data.
Transcript
cr;
show: data printString.

The final method that you need to implement is the free method. This method frees
up the DdeClient.
free
"Free up the dde client resource."
ddeClient free.

Discussion of the DdeClient


For an application to become a DDE client, it simply creates an instance of the
DdeClient class and connects it to a DDE server. The connection message to a DDE
server includes the server name and the name of the topic the client is interested
in. A DDE client can be established by running the following code:
ddeClient := DdeClient new.
ddeClient connect: serverName topic: topicName.

where serverName and topicName are instances of String.

Note: Once a DdeClient connects to a server for a particular topic, it cannot connect
to a different server or a different topic on the same server, unless it first
terminates the existing connection and then reconnects to a new server, or
topic, or both.

Chapter 12. Dynamic data exchange 359


Queries
A client application can query for information about the DDE servers currently
running. There are three methods of DdeClient that provide data on all of the
available DDE servers:
queryAll
Queries all servers for all possible topics, and returns a Dictionary where
the key is the application name and the value is a Set containing all of the
topic names supported by that application.
queryServersFor:
Queries all servers for a specified topic, and returns a Set containing the
names of the applications that handle the topic.
queryTopicsFrom:
Queries a particular named server for all of the topics it supports, and
returns a Set containing the names of the topics handled by that server.

Requesting data
A client application can request a specific data item from the server using the
requestData:format: message:
result := ddeClient requestData: item format: format

The format specification tells the server what format the client is interested in. If
the server does not respond with data, the result is nil.

Hot- and warmlinking to items


A client application might want a DDE server to notify it when a particular data
item changes. It does this by sending one of two messages to the DdeClient:
ddeClient hotLink: item format: format.

or
ddeClient warmLink: item format: format.

The format specification tells the server what format the client is interested in. The
hotLink:format: or warmLink:format: messages return true to indicate that the server
supports the data item in the specified format. However, if either message returns
false, then the server either does not support the data item or it does not support
the specified format for the data item.

For a client application to receive notification of a change for a hotlinked data


item, it must register the DdeNdataCallback with its DdeClient object. When the
hotlink callback runs, the actual changed data is passed in the DdeCallbackData
object where it can be retrieved with the data message. For example, to register the
DdeNdataCallback, a client application uses:
ddeClient
addCallback: DdeNdataCallback
receiver: self
selector: #hotLink:clientData:callData:
clientData: nil

The clients implementation of the hotlink callback method looks like:


hotLink: ddeClient clientData: clientData callData: callData
"The data at the server has changed. Go get the data."
| item format string data|

item := callData item.


format := callData format.
data := callData data.

360 IBM Smalltalk: Programmers Reference


string
data notNil
ifTrue: ['Data arrived %1 %2']
ifFalse: ['Data unavailable %1 %2']
Transcript
cr;
show: (string bindWidth: item with: format).

Likewise, for a client application to receive notification of a change for a


warmlinked data item, it must register the DdeNchangeCallback with its DdeClient
object. However, unlike a hotlink callback, the changed data item is not passed to
the callback in the DdeCallbackData object. To obtain the changed data, the client
application must explicitly request it from the server by using the
requestData:format: message on the DdeClient passed to the callback as the first
parameter. This message returns the data from the server if it is available,
otherwise it returns nil. The following code warmlinks the client application to an
item and processes the event when that item changes at the server:
ddeClient
addCallback: DdeChangeCallback
receiver: self
selector: #warmLink:clientData:callData:
clientData: nil

and
warmLink: ddeClient clientData: clientData callData: callData
"The data at the server has changed. Go get the data."
| item format string data|
item := callData item.
format := callData format.
data := ddeClient requestData: item format: format.

string :=
data notNil
ifTrue: ['Data arrived %1; %2']
ifFalse ['Data unavailable %1; %2']
Transcript
cr;
show: (string bindWidth: item with: format).

Coldlinking items
A client application unlinks from an item previously hot- or warmlinked by
sending the coldLink:format: message to a DdeClient:
ddeClient coldLink: anItem format: aString.

This message returns true if the item successfully unlinks. It returns false if the item
cannot be unlinked. An item cannot be unlinked if it was never linked, or if the
item, its format, or both did not exist on the server. After this message is sent, the
client application is no longer notified of updates to that item from the server.
However, it can still use the requestData:format: method on the DdeClient to query
the server for the data item.

Sending data to the server


A client application can send data to the DDE server by sending the
sendItem:value:format: message to the DdeClient object:
ddeClient sendItem: item value: value format: format.

The form of this message is the same as sendItem:value:format: of a DdeServer object.


Here, true is returned if the server accepts the data; otherwise, false is returned.

Chapter 12. Dynamic data exchange 361


Executing commands at the server
A client application can ask a server to run a command by sending the message
execute: to the DdeClient object along with a string containing the command to be
run:
ddeClient execute: 'someCommand param1 param2'

Here, true is returned if the server runs the command, and false is returned if it
cannot.

Formats of data transferred between DDE servers and clients


Any data item transferred between DDE servers and clients must have a
specification of its format so that it can be properly interpreted by the other side of
the connection. In IBM Smalltalk each format is described by a Smalltalk String.
Here are the predefined formats available for both the OS/2 and Windows
platforms:
v String
v Bitmap
v MetaFilePict
v Sylk
v Dif
v Tiff
v Dib
v Palette
v OwnerDisplay
v DspText
v DspBitmap
v DspMetaFilePict
v Rtf
v Link

The data to be transferred, with one exception, must be provided to the respective
DDE object as a ByteArray. For example, if a DDE server is serving up bitmap data,
then the server application provides the DdeServerManager a format specification of
Bitmap and a ByteArray object containing the bitmap data. The only exception is
the String format where the data is always provided to the DDE objects as a
Smalltalk String.

Callbacks for handling DDE events


Each DdeClient and DdeServerManager expects to have an application handle the
DDE events as they occur (unless default processing is used). These events are
handled by callbacks. For each possible DDE event, the application that creates a
DdeClient or DdeServerManager registers one of its methods as the callback to
handle that event. To register a callback, the application sends the message
addCallback:receiver:selector:clientData: to the respective DdeClient or
DdeServerManager.

The callback argument of this message identifies the particular DDE event that is
to be hooked. The receiver argument identifies the object that will be sent the
callback message, which is specified with the three-parameter message selector in
the selector argument. The clientData argument specifies optional data that is
passed to the callback method when it is run. The names identifying the DDE
events, or callbacks, are defined in the pool dictionary DdeConstants. The following
table identifies the callback names for the DdeServerManager.

362 IBM Smalltalk: Programmers Reference


Table 44. DdeServerManager callbacks
Callback Explanation
DdeNcoldlinkCallback A client is requesting to terminate a link to an item.
DdeNwarmlinkCallback A client is creating a warmlink to a data item.
DdeNhotlinkCallback A client is creating a hotlink to a data item.
DdeNpokeCallback A client is sending data to the server.
DdeNinitiateCallback A client is trying to establish a connection to the server.
DdeNexecuteCallback A client is requesting the server to run a command.
DdeNrequestCallback A client is requesting data from the server.
DdeNterminationCallback The client is terminating the connection with the server.

The following table identifies the callback names for the DdeClient.
Table 45. DdeClient callbacks
Callback Explanation
DdeNchangeCallback The item at the server has changed. The client must request
the data from the server.
DdeNdataCallback The item at the server has changed and the data is available
from the DdeCallbackData object through the data message.
DdeNterminationCallback The server is terminating the connection with the client.

Thus, to set up a DdeServerManager to handle the DDE run callback, use code like
the following:
aDdeServerManager
addCallback: DdeNexecuteCallback
receiver: self
selector: #execute:clientData:callData:
clientData: nil.

All DDE callbacks are handled in the same manner as Common Widget callbacks.
When a callback runs, its first parameter is the particular DdeServer or DdeClient
object that originated the event; the second parameter is the client data object that
was provided when the callback was registered; and the last parameter is a
DdeCallbackData object that contains the information about the event (that is, any
data associated with the event), the current server name, the current topic, and so
on. From the example above, the callback method that responds to the DDE run
event looks like this:
execute: aDdeServer clientData: clientData callData: callData
"A client has asked to run a command. Print the command in the
transcript and set the return value to true to indicate that the
command was run."
Transcript
cr;
show: ('Execute : %1' bindWith: callData execute).
callData returnValue: true.

Return values
A method responding to a DdeServerManager callback can return information back
to the source of the event through the returnValue attribute of a DdeCallbackData
object. This value can be set through the returnValue: message. A return value of nil

Chapter 12. Dynamic data exchange 363


(the default) causes default processing to be performed by the DdeServerManager.
The following table describes the return values for DdeServerManager callbacks.
Table 46. DdeCallbackData return values for handling DdeServerManager Callbacks
Callback Return value: true Return value: false Return value: nil
DdeNcoldlinkCallback The link has been broken. The link has not been If there is a link to the
broken. item, then break it.
DdeNwarmlinkCallback The item and format are The item and format are not If the item is in the
supported. supported. default database, then
create a link to it.
DdeNhotlinkCallback The item and format are The item and format is not If the item is in the
supported. supported. default database, then
create a link to it.
DdeNpokeCallback The server accepts the data. The server rejects the data. The server rejects the
data.
DdeNinitiateCallback N/A. To connect the DDE N/A. To reject the connect If the server name and
client send the message request do not send the the topic to which the
notify:ClientOfSupportFor: to message DDE client is attempting
the DdeServerManager notify:ClientOfSupportFor: to to connect exist in the
parameter of the callback. the DdeServerManager default database, then a
parameter of the callback. connection is made.
DdeNexecuteCallback The server can run the The server cannot run the The server cannot run the
command. command. command.
DdeNrequestCallback The data has been sent to The data has not been sent If the data is in the
the client. Send the to the client. The IBM default database, then it
sendItem:value:format: Smalltalk DDE subsystem is sent to the client.
message to the DdeServer sends a negative Otherwise, a negative
parameter of the callback. acknowledgment to the acknowledgment is sent
client. to the client.
DdeNterminationCallback N/A N/A N/A

For the DdeClient callbacks DdeNchangeCallback, DdeNdataCallback, and


DdeNterminationCallback, the return value is ignored whether the return value is
true, false, or nil.

DDE protocols
The following sections describe public protocols for these classes:
v DdeServerManager
v DdeClass
v DdeServer
v DdeClient

DdeServerManager public class methods


name: Answers a new DdeServerManager with the specified name (a String).
Return value: An instance of DdeServerManager.

DdeServerManager public instance methods


addCallback:receiver:selector:clientData:
Adds a callback to one of the receivers callback lists.

364 IBM Smalltalk: Programmers Reference


Generally speaking, a DdeServerManager expecting to interact with an
application declares one or more callback lists as resources, and the
application adds to these callback lists the callbacks that are invoked
whenever the predefined callback conditions are met. Callback lists are
resources, so that the application can set or change the function that is
invoked.
Callbacks are not necessarily invoked in response to any event; a
DdeServerManager can call the specified routines at any arbitrary point in
its code, whenever it wants to provide a hook for application interaction.
For example, all DdeServerManager objects provide a terminationCallback
resource to enable applications to interpose a routine to be run when a
DDE connection is broken.
This message adds a new callback to the end of the callback list. A callback
is invoked as many times as it occurs in the callback list.
callbackName
The resource name of the callback list to which the callback is to be
appended.
receiver
The object to send the callback message to.
selector The three-parameter message selector to send.
clientData
An object to be passed to the receiver of the callback message as
the clientData parameter when the callback is invoked, or nil.
addItem:topic:value:format:
Adds an item to the database.
item A String identifying the name of the item.
topic A String identifying the topic to which the item belongs.
value The data that is named by item. If the format is String, the value
must be a Smalltalk String, otherwise it must be a ByteArray.
format A String identifying the format of the data.
free Frees any allocated resources. The DdeServerManager cannot be used at any
time after this message is sent.
name Answers a String that is the name of the receiver.
notifyClientOfSupportFor:
Notifies the client attempting to connect that the receiver supports the
specified topic.
topicString
A String identifying the name of the topic.
removeAllCallbacks:
Deletes all callbacks from a callback list.
This message removes all the instance callback messages identified by the
specified callback name, regardless of the value of the clientData associated
with each message. This is in contrast to removeCallback:..., which removes
the specified callback only if a specified clientData argument also matches.
callbackName
The resource name of the callback list from which the callback is to
be removed.

Chapter 12. Dynamic data exchange 365


removeCallback:receiver:selector:clientData:
Deletes a callback from a callback list.
This message removes a callback message identified by callbackName.
The callback is removed only if both the callbackName and clientData match
a callback/data pair in the list. No warning message is generated if a
callback to be removed fails to match a callback or clientData in the list.
Use removeAllCallbacks:... if you want to remove a particular callback
regardless of the value of its clientData.
callbackName
The resource name of the callback list from which the callback is to
be deleted.
receiver
The object to match against the callback receiver in the callback list.
selector The three-parameter message selector which is to be used to match
against the callback in the callback list.
clientData
The object to match with the clientData object in the callback list
entry.
removeItem:topic:format:
Removes an item from the database. Answers false if the item was not
removed, or answers true if the item was removed.
item A String identifying the name of the item.
topic A String identifying the topic to which the item belongs.
format A String identifying the format of the data.

Return value:
true Item was removed.
false Item was not in the database and could not be removed.
servers Answers a set of DdeServers that are all the managers current servers (that
is, connections).
testMode
Answers a Boolean indicating the current test mode.
A Windows limitation prevents a DDE client from seeing servers provided
from the same program (.exe).
If you need to see DDE servers created from the same image as the DDE
client, set testMode to true. While testMode is true other clients cannot see
the servers from this program (.exe).
Return value:
true Test mode is on.
false Test mode is off.
testMode:
Sets the current test mode. If the answer is true, it turns the test mode on.
If false, it turns the test mode off.
A Windows limitation prevents a DDE client from seeing servers provided
from the same program (.exe).

366 IBM Smalltalk: Programmers Reference


If you need to see local servers, set testMode to true. While testMode is true,
other clients can not see the servers from this program (.exe).
timeout
Return an Integer indicating the number of milliseconds before a server
times out.
timeout:
Sets the timeout value for any new connections.
numberOfMilliseconds
An Integer number of milliseconds.
updateItem:topic:value:format:
Updates an item in the database if it exists. Then updates any links to that
item that any DDE clients might have. The links to the item are updated
even if the item is not in the default database.
item A String identifying the name of the item.
topic A String identifying the topic to which the item belongs.
value The data that is named by item. If the format is String, then value
must be a Smalltalk String, otherwise it must be a ByteArray.
format A String identifying the format of the data.

DdeClass public instance methods


addCallback:receiver:selector:clientData:
Adds a callback to one of the receivers callback lists.
Generally speaking, a dde object expecting to interact with an application
declares one or more callback lists as resources; the application adds to
these callback lists the callbacks that are invoked whenever the predefined
callback conditions are met. Callback lists are resources, so that the
application can set or change the function that is invoked.
Callbacks are not necessarily invoked in response to any event; a dde object
can call the specified routines at any arbitrary point in its code, whenever
it wants to provide a hook for application interaction. For example, all
dde objects provide a terminationCallback resource to enable applications to
interpose a routine to be run when the DDE instance is disconnected.
This message adds a new callback to the end of the callback list. A callback
is invoked as many times as it occurs in the callback list.
callbackName
The resource name of the callback list to which the callback is to be
appended.
receiver
The object to send the callback message to.
selector The three-parameter message selector to send.
clientData
An object to be passed to the receiver of the callback message as
the clientData parameter when the callback is invoked, or nil.
connected
Answers a Boolean indicating whether or not the receiver is connected to a
remote DDE object.
Return value:

Chapter 12. Dynamic data exchange 367


true Receiver is connected.
false Receiver is not connected.
disconnect
Disconnect the DDE conversation. This causes a DdeNterminationCallback
event.
removeAllCallbacks:
Deletes all callbacks from a callback list.
This message removes all the dde instances callback messages identified by
callbackName, regardless of the value of the clientData associated with each
message. This is in contrast to removeCallback:..., which removes the
specified callback only if a specified clientData parameter also matches.
callbackName
The resource name of the callback list to which the callback is to be
removed.
removeCallback:receiver:selector:clientData:
Deletes a callback from a callback list.
This message removes a callback message identified by callbackName.
The callback is removed only if both the callback object and clientData
match a callback/data pair in the list. No warning message is generated if
a callback to be removed fails to match a callback or clientData in the list.
Use removeAllCallbacks:... if you want to remove a particular callback
regardless of the value of its clientData.
callbackName
The resource name of the callback list from which the callback is to
be deleted.
receiver
The object to match against the callback receiver in the callback list.
selector The three-parameter message selector which is to be used to match
against the callback in the callback list.
clientData
The object to match with the clientData object in the callback list
entry.
timeout
Answers an Integer indicating the timeout value in milliseconds.
timeout:
Sets the timeout value for this DDE conversation.
numOfMilliseconds
An Integer number of milliseconds.

DdeServer public instance methods


disconnect
Disconnect this DDE conversation. This causes a DdeNterminationCallback
event.
notifyClientOfSupportFor:
Notifies the client attempting a connection that the receiver supports the

368 IBM Smalltalk: Programmers Reference


specified topic. If the server wants to notify the client that multiple items
are supported, this message can be sent multiple times, once for each topic
supported.
topicString
A String identifying the topic.

A new DdeServer is created to support this connection.


sendItem:value:format:
Sends data to the client. Answers true if the data was received by the
client, false otherwise.
item A String to identify the data.
value The data that is to be sent to the client. If the format is String, the
value must be a Smalltalk String. Otherwise, value must be a
ByteArray.
format A String identifying the format that the data is in.

Return value:
true Client received the item.
false Client did not receive the item.

DdeClient public instance methods


coldLink:format:
Tells the server to disconnect a link.
item A String identifying the item to disconnect.
format A String identifying the format of the item to be disconnected.

Return value:
true The server disconnected the link to the item.
false The server cannot disconnect the link to the item.
connect:topic:
Attempts to connect to a DDE server identified by applicationString with
topic topicString. Only the first server to acknowledge is connected. All
other connections are terminated. Processing of the connections from the
servers happens elsewhere. Answers true if the receiver is connected.
Otherwise, answer false.
applicationString
A String identifying the name of the application to connect to.
topicString
A String identifying the name of the applications topic to be
connected to.

Return value:
true The connection was made.
false The connection cannot be made.
execute:
Asks the server to run a command string on behalf of the receiver.
Answers true if the server ran the command. Otherwise, answers false.

Chapter 12. Dynamic data exchange 369


commandString
A String identifying the command to be run by the server.

Return value:
true The server runs the command.
false The server cannot run the command.
free Frees any allocated resources. After this message is sent, the receiver is
invalid and cannot be used.
hotLink:format:
Attempts to create a link to item so that when the item is updated by the
server, a callback (DdeNdataCallback) is called, sending the data as part of
the callback.
Answers a Boolean indicating whether the server accepted the request.
item A String identifying the name of the item to hotlink to.
format A String identifying the format that the data should be in when it
comes back from the server.

Return value:
true The item is hotlinked.
false The item cannot be hotlinked.
queryAll
Queries all servers for all topics. Answers a Dictionary of Sets with the
application name as the key and a Set of topic names as the value.
queryServersFor:
Queries all servers for a topic. Answers a Set of applications (instances of
String) that handle the topic.
topicString
A String naming the topic of interest.
queryTopicsFrom:
Queries a server for all topics that it supports. Answers a Set of topics
(instances of String) that are handled by the application.
applicationString
A String identifying the name of the server.
requestData:format:
Requests data from the server.
item A String identifying the name of the item to retrieve.
format A String identifying the format that the data should be in when it
comes back from the server.

Return value:
nil The server could not provide the data.
String The format requested was String and, therefore, the data from the
server is a Smalltalk String.
ByteArray
The format requested was something other than String and
therefore the data from the server is a ByteArray.

370 IBM Smalltalk: Programmers Reference


send:value:format:
Sends data to the server. This is a DDE poke.
item A String identifying the name of the item to send to the server.
value The data that is to be sent to the client. If the format is String, the
value must also be a Smalltalk String. Otherwise, value must be a
ByteArray.
format A String identifying the format that the data should be in when it
is returned from the server.

Return value:
true The server accepts the data.
false The server rejects the data or a time out has occurred.
warmLink:format:
Attempts to create a link to item so that when the item is updated by the
server, a callback (DdeNdataCallback) is called. The data is not sent as part
of the callback.
item A String identifying the item to disconnect.
format A String identifying the format of the item to be disconnected.

Return value:
true The item is warmlinked.
false Item cannot be warmlinked.

When the ENVY/Image DDE Examples configuration map is loaded, one of the
applications that is loaded is DdeExamples. This application contains a number of
classes that facilitate the testing of the DDE classes. The loading of this application
requires that the ENVY/Swapper configuration map be loaded.

Protocols common to DdeServer and DdeClient


The following protocols are common to DdeClient and DdeServer:

Timeouts
Both DdeClient and DdeServer objects can associate timeout values with their DDE
conversations. Because the IBM Smalltalk DDE model is synchronous, the timeout
values are used to time out responses to DDE requests across the connection. If a
response to a DDE requests timeout, then the value returned from the request
message is either nil or false, depending upon the context of the particular request
message. In other words, a timeout looks like a failure for the request message.
The timeout value can be set with the timeout: message in the following way:
ddeClient timeout: numberOfMilliseconds

or
ddeServer timeout: numberOfMilliseconds

The DdeServerManager also has a timeout: message that sets the timeout value for
any new DdeServer connections that are made.

The default timeout value is set to 1000 milliseconds (1 second).

Chapter 12. Dynamic data exchange 371


Disconnect
Both the DdeClient and the DdeServer can disconnect the DDE conversation at any
time if they are sent the disconnect message:
ddeClient disconnect

or
ddeServer disconnect

Free
Any DdeClient or DdeServerManager must be sent the free message when they are
no longer required by their respective applications. This cleans up and releases any
operating system resources that these objects have allocated. For example:
ddeClient free.

or
ddeServerManager free.

Converting Smalltalk objects to a ByteArray and back


The following section provides information for these earlier sections:
v An example DDE server that uses default processing on page 344
v Discussion of the DdeServerManager on page 352
v An example DDE client on page 358

The example method DdeTimeServer>>flatten: (in the DdeExamples application)


converts a Smalltalk object into a ByteArray. The code to do this (using the IBM
Smalltalk Swapper) is:
flatten: anObject
"Flatten anObject into a ByteArray."
| aByteArray size dumper |
dumper := ObjectDumper new.
size := dumper totalSizeBeforeUnload: anObject.
aByteArray := Array with: (ByteArray new: size).
dumper
unload: anObject
intoByteObjects: aByteArray
offsetsIntoByteObjects: 0
maximumLimit: size
errorStream: Transcript.
|aByteArray at: 1

The example method DdeTimeClient>>unflatten: (in the DdeExamples application)


converts a ByteArray object into a Smalltalk object. The code to do this (using the
Swapper) is:
unflatten: aByteArray
"Answers an object from a ByteArray.
Call the Swapper ObjectLoader to rebuild the object."
| bytes |
bytes := Array with: aByteArray.
|ObjectLoader new
loadFromByteObjects: bytes
offsetsIntoByteObjects: 0
errorStream: nil.

Test cases
You can run the following test cases to exercise some of the DDE functions
described in this chapter.

372 IBM Smalltalk: Programmers Reference


Spreadsheet
To run this test case evaluate the following:
DdeSpreadsheetClient new open

This opens up a window with a menu named DDE. Next start Microsoft Excel
(Excel version 4.0 is used in this example) and fill in two cells with numbers.
Under the Microsoft Excel menu Formula choose Define Name. This option
enables these cells to be named x and y, respectively. Save the spreadsheet with the
name ddesrvr.xls. (The DDE client looks for a server with this name.)

The window in this test case provides a menu for the following:
v Connecting to the spreadsheet
v Hotlinking, warmlinking, and coldlinking to the two items
v Requesting the items from the server

There is also a menu option for bringing up a dialog showing the available servers,
topics, and items.

Spreadsheet window coordinates


To run this test case evaluate the following:

This opens up a window such that whenever the mouse passes over the window,
the x and y position of the mouse is displayed in the upper left corner of the
window. The name of the server is DdeExample, and there is a topic named
Mouse. Under this topic there are two items xpos and ypos.

Next, start up Microsoft Excel and in a cell enter:


=DdeExample|Mouse!xpos

And in another cell enter:


=DdeExample|Mouse!ypos

As the mouse is moved over the Smalltalk window, the coordinates change in the
spreadsheet.

Two windows exchanging data


To run this test case, evaluate the following:
DdeTestClient new open.
DdeTestServer new open.

This opens up two windowsone a DDE client and the other a DDE server. Both
windows are able to interact with each other for the exchange of information
through DDE.

The menu items on the DDE server enable the server to update the item. The
menu items on the DDE client enable the client to connect to the server,
hotlink/warmlink/coldlink to the data item, request data from the server, poke
data to the server, and ask the server to run a command. The client can also bring
up a dialog showing the available servers, topics, and items.

When the server sends the item, it uses the IBM Smalltalk Swapper to flatten the
Smalltalk object into a ByteArray, and the client uses the Swapper to turn the
ByteArray back into a Smalltalk object before displaying it in the Transcript
window.

Chapter 12. Dynamic data exchange 373


Updating time and date
To run this test case, evaluate the following:
DdeTimeServer new.
DdeTimeClient new.

This creates a server and a client that do not have an interface but are primarily for
use as example code. The server in this case hooks to a timer and updates a time
and date item. The client connects to the server and hotlinks to the time and date.
When the items are updated, the client displays the time/date in the Transcript
window.

The time and date items in this test case are in the Smalltalk Object format. As in
Two windows exchanging data on page 373, the client and server use the
Swapper to convert a ByteArray to/from a Smalltalk object.

Updating time in the String format


To run this test case, evaluate the following:
DdeTimeServer2 new.

This creates a server as in Updating time and date, but only the current time in
the String format is supported. The client from Updating time and date does
not work with this server. Instead, use Microsoft Excel by entering into a cell:
= Timer|Time!time

The current time appears in the cell. It is in a floating point format but can be
changed by choosing Format/Number from the menu.

Platform-specific support
The IBM Smalltalk DDE subsystem was developed as an extension to the DDE
support provided by IBM OS/2 and Microsoft Windows. The following figure
illustrates the layers provided by the IBM Smalltalk DDE subsystem.

Application

IBM Smalltalk DDE

IBM Smalltalk
platform interface
classes and methods

IBM Smalltalk

The application programmer can skip the provided IBM Smalltalk DDE layer and
program directly to the operating system support mechanisms in the platform
interface layer.

In the platform interface layer, objects have been created for all C data types for
both IBM OS/2 and Microsoft Windows. These data types include items such as
DDEACK, DDEADVISE, DDEDATA, and HWND for Microsoft Windows, or
DDEINIT, DDESTRUCT, CONVCONTEXT, and HWND for IBM OS/2. These data
objects are named by prefixing the data type name with OS, and applying

374 IBM Smalltalk: Programmers Reference


bicapitalization rules. For example, HWND becomes OSHwnd. Additionally, each
operating system C function has been converted to a corresponding Smalltalk
method. To understand this conversion, consider the standard OS/2 function
WinDdePostMsg:
WinDdePostMsg( hwndDest, hwndSource, msg, ddeStruct, 1)

This function has been converted to the following:


OSHwnd>>winDdePostMsg: hwndFrom wm: wm pddest:pddest
flOptions:flOptions

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


v If there is no Smalltalk object that corresponds to the first parameter, then the
receiver of the message is OSCall. Otherwise, the receiver is the first parameter
of the function.
v The Smalltalk message selector is formed from the C function name and the
remaining parameter names.

Chapter 12. Dynamic data exchange 375


376 IBM Smalltalk: Programmers Reference
Chapter 13. National Language Support
National Language Support (NLS) can be very important. Todays international
business climate often requires that application designers support the global
marketplace and invest in NLS enablement in the initial design stages. IBM
Smalltalk supports this process.

This chapter describes:


v IBM Smalltalk National Language Support
v Tools for developing international software

Overview of IBM Smalltalk National Language Support


IBM Smalltalk implements common NLS protocols modeled after the POSIX.1
Standard for Information Technology (IEEE Std. 1003.1-1990). This ANSI-C based
approach has also been adopted by X Release 5.

IBM Smalltalk adds NLS support classes and extends existing classes to support
platform-specific character sets, collation order, currency, date format, and time
format.

NLS concepts and definitions


This chapter assumes familiarity with the following terms.
Coded character set
Characters that define how integer code points are interpreted and
displayed. A code point is simply a numeric valueits semantic
interpretation (for example, a vowel, whitespace, or punctuation) and
graphical interpretations (that is, associated glyph) are defined by the
coded character set associated with the code point.

Note: A coded character set is sometimes referred to as a code page or


code set. For the purposes of this chapter these terms are
synonymous.
Composite message
A localized message composed of several smaller localized strings and
assembled at run time (typically using message templates).
Externalization
The process of separating language- and culture-dependent information
from the processing of an application. For example, messages displayed to
users are often hard-coded into applications and limit the usefulness of the
application outside of the language and culture for which it was designed.
The process of externalization enables the application to be easily adapted
for use in numerous locales.
External message dictionary
A dictionary (usually a pool dictionary) that contains localized messages
for a particular locale and character set, stored in a disk file.
Indexed external message
A localized message that is further identified and accessed by an integer
key and is stored in a disk file.

Copyright IBM Corp. 1994, 2000 377


Internationalization
The process of designing an application in a manner that is independent of
the local customs and culture in which it was developed. In practice, this
means ensuring that the application makes no assumptions about
language, local customs, or character set, thus creating an application that
is location-neutral.

Note: Internationalization is referred to as NL-enablement or


NLS-enablement. An internationalized application is often termed
NLS-enabled.
Language
The whole body of written words and system of combining words used by
a particular group of people; for example, English or French.
Locale
A language/territory combination that dictates conventions for the
presentation of information, such as collation order, character classification,
monetary and numeric formats, and date and time formats. Both language
and territory are necessary to identify a locale because the same language
might be spoken in many geographic areas, each of which has distinct
cultural conventions.

Note: A country might, but does not necessarily, identify a particular


locale. For example, Canada supports two distinct locales: French
Canada and English Canada. Simply knowing that an application is
destined for Canada is insufficient.
Localization
Refers to the process of adapting software to a particular locale (language,
territory, and character set). An internationalized application contains no
code that is dependent on the end users language, the characters needed
to represent the language, or any formats such as time, date, and currency
that the user expects to see and interact with. Because the language- and
culture-dependent information is separate from the application source
code, the application does not need to be rewritten or recompiled to be
marketed in different countries. Instead, the only requirement is for the
externalized information to be localized by translation experts.
Localized message
A message string that has been designed for a specific locale and character
set combination. Localized messages are referenced in application code
through identifiers that are independent of locale and character set.
Message catalog
A file that resides in secondary storage that can contain indexed external
messages and external message dictionaries. A single message catalog
might contain indexed external messages and external message dictionaries
for multiple locale and character set combinations.
Message template
A string template containing field identifiers that are replaced with string
arguments to form a composite message. For example, binding the
message template This is a %1 message template with the string useful
produces the composite message This is a useful message template.

378 IBM Smalltalk: Programmers Reference


Territory
A linguistic or cultural entity that might or might not correspond to a
geographic area; for example, Arabic, Canadian, Japanese, Mexican, and
YugoCroatian.

The POSIX locale model


The POSIX locale model divides locale-dependent information into six locale
categories that assist in the development of internationalized applications. Each
locale category controls the behavior of a predefined set of operations and is
associated with a particular locale. Locale categories are represented by symbolic
constants describing their function and are prefixed by LC_. The locale categories
defined by the POSIX model are listed in the following figure, along with a brief
description of the responsibilities and functions controlled by each category.

LC_COLLATE Responsibility: Controls string collation (sort order).


Functions: strcoll(), strxfrm().

Responsibility: Controls character classification & conversion.


LC_CTYPE Functions: isalpha(),islower(),ispunct(),isspace(),isupper(),toupper(),tolower().

LC_MONETARY Responsibility: Controls printing of monetary values.


Functions: localeconv().
POSIX Locale
Responsibility: Controls the language in which messages are displayed.
LC_MESSAGES
Functions: nl_info() / langinfo().
Responsibility: Controls the printing of numeric values.
LC_NUMERIC
Functions: localeconv().

Responsibility: Controls the printing of dates and times.


LC_TIME
Functions: strfmtime().

Each locale category controls the behavior of a predefined set of operations and
ensures that the operations under its control behave properly for the locale
associated with the category. For example, the LC_COLLATE category maintains
information pertinent to string sorting, and, if configured for the U.S. English
locale, ensures that strings are sorted according the rules for U.S. English.

Locale categories function independently within an application and need not be


associated with the same locale. For example, it is possible for an application to
format monetary quantities according to the rules for U.S. English and to display
messages in French by setting the LC_MONETARY and LC_MESSAGES categories
appropriately.

Each process (application) maintains its own set of locale category information, and
can configure the locale categories appropriately for the needs of the application.
The POSIX locale model provides a well defined set of locale-sensitive operations
and a mechanism for controlling the behavior of these operations. By using these
locale-sensitive operations, the same internationalized application can support
several locales simply by altering the settings of the locale categories.

Overview of internationalization
A perfect internationalized application contains no code that is dependent on the
end users language, on the characters needed to represent the language, or on any
formats such as time, date, and currency that the user expects to see and interact
with. In order to minimize the cost of localizing an application (adapting it for a
particular locale) the following elements should not be represented explicitly in
application code:
v Displayed message strings, including the following:
Help text
Menu items
Prompts
Labels
Error messages

Chapter 13. National Language Support 379


v Other locale-dependent information, including the following:
Bitmaps
Icons
Geometry

When language- and culture-dependent information is separate from the


application source code, the application does not need to be rewritten to be
marketed in different countries. Instead, the only requirement is for the
externalized information to be localized by translation experts.

Overview of localized messages


Localized messages are dependent upon the language and territory in which they
are intended to be used, and on the availability of the characters used to represent
their contents. This means that localized messages are associated with a specific
locale and character set. Character sets and fonts are closely related, and are
described in detail in the latter part of this section.

The following figure shows the relationship between localized messages, locales,
and character sets:

Localized Message Contains Message text


e.g. This is an english message
depends-on

Locale Character Set


e.g. *(english us) e.g. iso5658-1
CLDT subsystem Common Graphics Subsystem

The Common Language Data Types (CLDT) subsystem provides the class Locale to
help determine the current language and territory. These values can be obtained by
evaluating Locale current language and Locale current territory. The language
and territory of a localized message determine what message text should be
displayed.

The IBM Smalltalk Common Widgets subsystem enables you to explicitly specify
the font used by a particular widget. If localized text is to be displayed correctly, it
is critical that you be aware of the fonts (and their coded character sets) that are
used in widgets that display this text.

A coded character set defines a mapping between code points (Characters) and
glyphs. A character set, in conjunction with a locale, also defines the semantic
value of a Character (for example, whether the Character is uppercase, lowercase, a
digit, or so on).

380 IBM Smalltalk: Programmers Reference


The following figure shows the relationship between fonts, code points, and
glyphs.
Font
Character Set
(e.g. iso8859-1)

63 >
Code Point (Character) 64 ?
65 65 A Displayed Glyph
66 B
.
.
.

Other font information

In addition to font metrics such as size (for example, 11 point) and style (for
example, bold, italic) every font maintains a mapping from character values to
graphical representations, called a coded character set. The coded character set
associated with a font determines how an character value is translated into its
graphical representation.

The translation of code points into their visual representation might necessitate
that a single localized message for a particular locale be stored separately for each
supported character set. Consider the font -microsoft-courier new-bold-i-normal-0-
0-96-96-m-0-iso8859-1, whose character set is iso8859-1. In this font, the character
with the graphical representation is associated with the code point 224 (Character
value: 224). In character set ibm-437 the same visual representation is associated
with the code point 133 (Character value: 133). Thus, in order to display the text
la mode, a distinct localized message is required for each character set. It is the
developers responsibility to ensure that localized messages are appropriate for the
font of the widgets in which they are being displayed.

Note: The Common Widgets subsystem allows you to specify the font used by a
particular widget explicitly. To display localized text correctly, you must be
aware of the fonts (and their character sets) that are used in widgets that
display this text.

The following code fragment demonstrates how to query a widget for the character
set used by its font. This code fragment assumes that the variable aWidget refers to
the widget whose font is being queried. Note that if the font being queried does
not conform to the X Logical Font Description (XLFD) naming convention the
CgLogicalFontDescription>>name: message can answer nil.
| aWidget fontName fontDescription |
...
fontName := aWidget fontList fontStruct name.
fontDescription := CgLogicalFontDescription name: fontName.

"If font is unknown, use iso8859-1 by default."


fontDescription isNil ifTrue: [|'iso8859-1'].
|fontDescription characterSet

Chapter 13. National Language Support 381


Overview of message catalogs
Applications that are expected to function in several locales must display localized
messages, and therefore require some facility to store and retrieve these messages
for a particular locale and character set. The POSIX.1 standard does not define any
mechanism for storing or retrieving localized messages; however, IBM Smalltalk
supports a mechanism for storing and retrieving localized messages modeled after
the message catalogs described by the X/Open Portability Guide.

An X/Open message catalog consists of a disk file, external to application code,


that can be translated to support a variety of locales and character sets. The
X/Open message catalog identifies each message numerically and provides
protocols for opening and closing message catalogs, and for retrieving localized
messages. Message catalogs are specified using a portable C-like text file, and are
compiled into a machine-specific (non-portable) message file.

In IBM Smalltalk, the basic mechanism for maintaining localized messages external
to the application is the NlsMessageCatalog class. Instances of NlsMessageCatalog use
the IBM Smalltalk Swapper as the basis of a platform-independent mechanism for
writing and retrieving messages to and from secondary storage. The
NlsMessageCatalog class provides protocols and a file structure designed specifically
for storing localized messages.

Unlike an X/Open message catalog, the NlsMessageCatalog allows localized


messages for several locales to be maintained in a single file. The
NlsMessageCatalog provides protocols for storing and retrieving Indexed External
Messages that are identified by numeric constants, and are used to reduce memory
requirements of an application. The NlsMessageCatalog also provides extended
capabilities for storing and retrieving External Message Dictionaries, which provide
an efficient mechanism for loading large numbers of messages.

The NlsMessageCatalog uses the following keys to identify localized messages:


language
A variable-length, lowercase, single-byte string defining the language of a
given locale. This value is taken from the IBM Smalltalk locale naming
conventions described in Table 52 on page 411. For example, english,
french.
territory
A variable-length, lowercase, single-byte string defining the region of a
given locale. This value is taken from the IBM Smalltalk locale naming
conventions described in Table 52 on page 411. For example, us, arabic.
coded character set
A variable-length, single-byte string describing the character set of the font
that is used by widgets to display the localized messages. This value is
platform dependent and is provided by the Common Graphics subsystem.
For example, ibm-437, iso8859-1.

Locating message catalogs


The X/Open UNIX standard defines an environment variable called NLSPATH that
specifies the directories to be searched for localized files. The Locale class provides
similar facilities for IBM Smalltalk applications through the NlsPath variable, which
contains a collection of directory paths to search for message catalogs.

382 IBM Smalltalk: Programmers Reference


The nlsPath and nlsPath: methods are provided to query and set the value of the
NlsPath variable. The value of the NlsPath variable is not affected by reinitializing
the current locale.

Note: All NlsMessageCatalog protocols that read from a specified path first assume
the path to be absolute. If the first attempt to open this file fails, it is
assumed to be a relative path name located in one of the directories
specified by Locale nlsPath. Therefore, once NlsPath is set, you need only
specify the name of the desired message catalog when using the
NlsMessageCatalog read operations described in the following section.

National Language Support classes


In IBM Smalltalk, the six locale categories defined by the POSIX model are
supported by the classes LCCollate, LCCType, LCMonetary, LCMessages, LCNumeric,
and LCTime. The class Locale provides access to the current settings of locale
categories, and protocols to update locale categories from values specified by the
operating system.

An instance of class Locale is created and initialized with the currently configured
platform locale information at image startup. This instance is known as the current
locale and can be accessed by sending current to the Locale class. The current locale
cannot be set explicitly, only initialized from operating system values.

The current locale maintains instances of the LCCollate, LCCType, LCMonetary,


LCMessages, LCNumeric, and LCTime classes that are used to control date and time
printing, character sorting, and string sorting. A detailed description of these
objects, and the protocols used to access them, is presented in the sections entitled
Obtaining Locale, LCCType, and LCCollate objects on page 384 and Obtaining
LCMessages, LCMonetary, LCNumeric, and LCTime objects on page 385. All
applications concerned with NLS enablement should use the information available
from the current locale.

Support for double-byte characters


This section describes the classes that provide support for double-byte characters.
This section is of particular interest to developers whose applications will be
deployed in languages such as Japanese where the size of the alphabet exceeds 255
characters and thus requires the use of double-byte characters.

Characters
Characters are defined to be unique objects that represent code points. Single-byte
characters are restricted to values in the range 0 to 255. Double-byte characters
have values in the range 0 to 65535. The interpretation of character values is
dependent on a coded character set. Several Character protocols (that is, the
protocols concerned with collation, character classification, and case conversion)
have been refined or extended to be consistent with National Language Support
requirements.

String classes
The classes String and DBString are used to represent sequences of characters in
IBM Smalltalk. DBString represents a sequence of Characters that can take values
between 0 and 65535 (that is, double-byte characters). Similarly, String represents a
sequence of Characters that can take values between 0 and 255 (that is, single-byte
characters). Both String and DBString support efficient character-based accessing
and indexing operations. Common Widgets and Common File System subsystem

Chapter 13. National Language Support 383


protocols answer and accept instances of String or DBString as appropriate for the
current locale and object being passed or returned.

IBM Smalltalk does not support Symbols containing double-byte characters.

Sending the message asDBString converts a String or DBString into a DBString.


Sending the message asSBString to an instance of String answers the receiver,
because it cannot contain double-byte characters. Sending asSBString to an instance
of DBString converts the receiver into a String if possible; however, this message
fails if the receiver contains characters with values greater than 255, in which case
nil is returned.

Attempting to store a Character with value greater than 255 into a String is an error.
IBM Smalltalk provides the Locale>>isDBLocale method to determine if a locale uses
double-byte characters. The isDBLocale message answers a boolean value indicating
whether or not the receiver locale uses double-byte characters.

A locale that supports double-byte characters should use DBString as its string
class. When sent to an instance of Locale, the preferredStringClass message answers
either String or DBString, whichever class is appropriate for the locale represented
by the receiver. For example, if the current locale is #(japanese japan), then
preferredStringClass answers DBString.

The preferredStringClass message is of particular use in creating streams used in


application code. For example, the practice of writing
WriteStream on: String new.

results in an exception if a double-byte character (a Character with value greater


than 255) is ever appended to this stream. The following code is portable, and uses
the string class appropriate for the current locale.
WriteStream on: Locale current preferredStringClass
new.

Obtaining Locale, LCCType, and LCCollate objects


The platform default locale is defined as the current set of international and
formatting values available from the operating system. This is the only locale that
is always guaranteed to be available.

If the information obtained from the operating system identifies the platform
default locale as one of the IBM Smalltalk supported locales (see the section
entitled Locales and platform representation on page 415), then the language and
territory fields of the current locale are set accordingly. If the platform default
locale cannot be identified, the territory and language fields are set to the string
<unknown>. It is possible that the information obtained from the operating
system might only partially identify a locale (that is, either the language or
territory). In such a case the value that cannot be determined is set to the string
<unknown>.

The class LCCType has only one valid instance in the image, and no protocols are
provided to create new instances explicitly. New instances of Locale and LCCollate
are obtained by sending new to the classes. These instances of Locale and LCCollate
can be localized manually using the protocols described in Manual localization
on page 412.

384 IBM Smalltalk: Programmers Reference


When the current locale is initialized, new LCCType and LCCollate objects are
created, initialized, and stored. The current LCCType and LCCollate objects can be
retrieved by sending the lcCType and lcCollate messages to the current locale
(Locale current).

Upon successful initialization of the current locale the relocalize message is sent to
every application and subapplication currently loaded in the image, in prerequisite
order (that is, Kernel first). Applications can use this mechanism to perform any
required locale-specific configuration.

Obtaining LCMessages, LCMonetary, LCNumeric, and LCTime


objects
When the current locale is initialized, new LCMessages, LCMonetary, LCNumeric,
and LCTime objects are created, initialized, and stored. The current LCMessages,
LCMonetary, LCNumeric, and LCTime objects can be retrieved by sending the
lcMessages, lcMonetary, lcNumeric, and lcTime messages to the current locale (Locale
current).

Multiple instances
IBM Smalltalk supports the use of multiple LCMessages, LCMonetary, LCNumeric,
and LCTime objects. Each instance of these classes maintains information for a
particular locale and can be created using the for: protocol. The argument to the for:
message is an array of two strings specifying the language and territory for which
information is to be retrieved.

New instances for the platform default locale


New instances of the LCMessages, LCMonetary, LCNumeric, and LCTime classes can
be created and initialized for the platform default locale using the for: protocol and
an array of two empty stringsthat is, #( )as the parameter. This platform
default value is always guaranteed to be available. The following code example
answers a new LCMonetary object initialized for the platform default locale.
LCMonetary for: #('' '')

New instances for different locales


New instances of the LCMessages, LCMonetary, LCNumeric, and LCTime classes can
be created, and initialized, for a particular locale using the for: protocol and an
array containing the language and territory strings of the requested locale as the
argument. For a complete list of valid language and territory combinations, refer to
Table 52 on page 411. You can see what language and territory combinations your
operating system supports by evaluating Locale knownLocales.

If the information for the requested locale is not available, the for: message answers
nil. Otherwise, the newly created and initialized object is answered. The following
code fragment queries the operating system for the monetary formatting
information for the territory 'us' and language 'english.' Note that the territory
and language strings must be in lowercase.
LCMonetary for: #('english' 'us')

Example usage of locale-specific information


The following method uses the current Locale and LCMessages, to print a list of
items using the locale-defined data separator:
printedItems: anIndexableCollection
"Print the items in anIndexableCollection separated by the data separator
for the current locale."

| stream sep |

Chapter 13. National Language Support 385


sep := Locale current lcMessages dataSeparator.
stream := WriteStream on: Locale current preferredStringClass new.
1 to: anIndexableCollection size - 1 do: [:index |
(anIndexableCollection at: index) printOn: stream.
stream nextPutAll: sep].
anIndexableCollection last printOn: stream.
|stream contents

NLS-enabled classes
Several base classes in the image make use of locale-dependent information held
by the current locale (Locale current). A summary of these classes follows:

Character (CLDT)
<=, <, >=, >
Collates characters according to the collation ordering for the U.S. English
locale on OS/2 3.0 and Microsoft Windows 3.11, or the collation ordering
for the C locale on X-Motif.
asLowercase, asUppercase
Performs case conversions according to the LCCType object associated with
the current locale (Locale current lcCType).
isAlphaNumeric, isDigit, isLetter, isLowercase, isPunctuation, isUppercase
Performs character classification according to the LCCType object associated
with the current locale (Locale current lcCType).

LCCollate (NLS)
compareCharacter:and:
Collates characters according to the LCCollate object associated with the
current locale (Locale current lcCollate).
compareString:and:
Collates strings according to the LCCollate object associated with the
current locale (Locale current lcCollate).

Date (CLDT)
printOn:
Prints the date in the format specified by the LCCTime object associated
with the current locale (Locale current lcTime).

String (CLDT)
<=, <, >= , >, sameAs:
Collates characters according to the collation ordering for the U.S. English
locale on OS/2 3.0 and Microsoft Windows 3.11, or the collation ordering
for the C locale on X-Motif.
asLowercase, asUppercase
Performs case conversions according to the LCCType object associated with
the current locale (Locale, current lcCType).

Time (CLDT)
printOn:
Prints the time in the format specified by the LCTime object associated with
the current locale (Locale current lcTime).

386 IBM Smalltalk: Programmers Reference


Locale-specific sorting
The String and Character collation operations use collation ordering for U.S. English
on Windows and OS/2 3.0, and the ordering for the C locale on X-Motif. You
should use the compareString:and: and compareCharacter:and: methods provided by
LCCollate to perform locale-specific sorting. You can use a SortedCollection to sort a
collection based on the current locale by specifying a sort block that uses the
current LCCollate instance. See the following example:
"Warning: do not retrieve the lcCollate from the locale inside the
sort block because if the locale is changed the Sorted Collection
may become inconsistent."
| lcCollate |
lcCollate := Locale current lcCollate.
|SortedCollection sortBlock: [:a :b |
(lcCollate compareString: a and: b) = 2 ].

Number formatting
Currency symbol, spacing, and sign locations
You format monetary values using a combination of the pSepBySpace, pSignPosn,
pCsPrecedes, nSepBySpace, nSignPosn, and nCsPrecedes values. The first three values
are used when formatting positive values, and the last three when formatting
negative values. Together these variables control the relative positions of the
currency symbol and sign indicator. Instances of the LCMonetary class are used to
hold these parameters for a particular locale.

The following table illustrates the use of these parameters in formatting the
non-negative monetary value 1.25. The same formatting rules are also used to
format negative monetary values by changing the positive sign character to a
negative sign character.

The pCsPrecedes value indicates the relative position of the currency symbol in
formatted monetary qualities.
0 The currency symbol follows the monetary quantity.
1 The currency symbol precedes the monetary quantity.

The SepBySpace value indicates if the currency symbol is separated from the
monetary quantity by a space in formatted monetary quantities.
0 No space separates currency symbol from monetary quantity.
1 A space separates currency symbol from monetary quantity.
2 A space separates currency symbol and positive sign if adjacent.

The pSignPosn value specifies the relative position of the positive sign string
formatted monetary quantities.
0 Parentheses enclose both the quantity and the currency symbol.
1 The positive sign follows the quantity and the currency symbol.
2 The positive sign follows the quantity and the currency symbol.
3 The positive sign immediately precedes the currency symbol.
4 The positive sign immediately follows the currency symbol.
Table 47. Numeric formats specified by currency symbol, spacing, and sign location
pSepBySpace
2 1 0

Chapter 13. National Language Support 387


Table 47. Numeric formats specified by currency symbol, spacing, and sign
location (continued)
psCsPrecedes = 0 pSignPosn = 0 (1.25 $) (1.25$) (1.25$)
pSignPosn = 1 +1.25 $ +1.25 $ +1.25 $
pSignPosn = 2 1.25$ + 1.25$ + 1.25$ +
pSignPosn = 3 1.25 +$ 1.25 +$ 1.25 +$
pSignPosn = 4 1.25 $+ 1.25 $+ 1.25 $+
pCsPrecedes = 1 psignPosn = 0 ($1.25) ($1.25) ($1.25)
pSignPosn = 1 + $1.25 + $1.25 + $1.25
pSignPosn = 2 $1.25 + $1.25 + $1.25 +
pSignPosn = 3 + $1.25 + $1.25 + $1.25
pSignPosn = 4 $+ 1.25 $+ 1.25 $+ 1.25

Numeric groupings
A grouping string is used to specify the size of each group of digits in formatted
monetary and numeric quantities. The grouping string consists of a series of
numbers separated by spaces. The first number in the string indicates the size of
the group of digits immediately to the left of the decimal indicator.

The following numbers have special meaning in the grouping string:


127 Perform no further grouping
0 Repeat last grouping

The effects of the grouping string on monetary formatting are most easily
visualized by example. The following table illustrates formatting the number
123456789 as a function of the grouping string, using a comma as the separator.
Table 48. Use of the grouping string to format numeric values
Grouping string Resultant formatted numeric quantity
3 123456,789
3 0 123,456,789
3 2 1234,56,789
3 2 127 1234,56,789
3 2 0 12,34,56,789
127 123456789
123456789

Locale change notification and the image startup sequence


A special SubApplication class protocol is provided to enable applications to
reconfigure in response to Locale initialization.
1. The current Locale is determined based on the platform default locale.
2. The message preStartUp is sent to all loaded (sub)applications in prerequisite
order.
3. The message localize is sent to all loaded (sub)applications in prerequisite order.
4. The message startUp is sent to all loaded (sub)applications in prerequisite
order.

388 IBM Smalltalk: Programmers Reference


Applications can override the localize method to perform any required
locale-specific configuration.

Note: The Locale class is initialized automatically at image startup, and can be
reinitialized manually by evaluating Locale initializeCurrentLocale. For
example, when using Windows 3.11, you might want to update the current
locale after changing the settings in the International control panel.

SubApplication class
localize
This message informs currently loaded Application and SubApplication
classes that the current locale has been changed. This message is sent to all
loaded Application and SubApplication classes on image startup, and each
time the current locale is successfully initialized (that is, by evaluating
Locale initializeCurrentLocale).

Tools for developing international software


This section describes the IBM Smalltalk tools that assist in the development of
internationalized applications by externalizing displayed messages such as help
text, menu items, and labels.

Loading externalization tools


Tools provided to assist in developing internationalized software are contained in a
configuration map entitled IBM Smalltalk NLS Externalization that includes the
following applications:
NlsExternalizationRuntime
This application provides the classes and protocols required for reading
localized text at runtime.
NlsExternalizationTools
This application provides additional protocols for creating files that contain
localized text, and for converting these files to platform resource formats.

Using message templates


Many applications use language-dependent text throughout to provide localized
menu selections, warning and error message descriptions, and help text. Often, the
display of language-dependent text requires the insertion of locale-dependent
string arguments into a message template. The processing used to construct a
composite message from various components can introduce unwanted language
dependencies and can act as a barrier to localization. The following example shows
a locale-specific way to construct a composite message:
displayWarningFor: userName operation: anOperation
"Display a warning message on the Transcript warning the user called
userName that the operation called anOperation cannot be performed."
Transcript
cr;
show: 'Sorry, ', userName;
show: ' I cannot perform', anOperation;
show: '!'.

This technique is commonly used in the construction of messages displayed to


users. This technique is a barrier to localization because it embeds
language-specific semantics in the processing used to construct the message. Some
languages might require that the contents of the userName field appear after the
anOperation field, or that a punctuation mark other than an exclamation mark (!) be

Chapter 13. National Language Support 389


used to terminate the message. The approach illustrated in the preceding example
cannot support these requirements and is not suitable for use in an
internationalized application.

IBM Smalltalk provides a mechanism for replacing tokens in a message template


with variable arguments. A template string is a String or DBString containing
constant text and token identifiers that are replaced with arguments specified by
the developer. Token identifiers consist of a percent character (%) and a numeric
field identifier in the range one to nine (1-9).

A template string is expanded based on arguments provided by the developer


using the bindWith:, bindWith:with:, bindWith:with:with:, bindWith:with:with:with:, or
bindWithArguments: methods. The numeric portion of the token identifier indicates
which argument should replace the token when the template is expanded to create
a composite message (for example, %1 corresponds to the first argument, %2
corresponds to the second argument, etc.). The double percent escape sequence
(%%) is replaced by a single percent character in the composite message.

A template string permits arguments to be printed selectively, and in arbitrary


order as illustrated in the following table. Although arguments are provided in a
fixed order, there is no restriction placed on their order of appearance in the
template string. Arguments that are not referenced in the template string are
ignored; an error is generated if a template string references a missing argument.
Table 49. Sample message templates and results
Sample template and arguments Resultant message
%1 %2 %3 bindWith: one with: two with: three one two three
%3 %2 %1 bindWith: one with: two with: three three two one
%1 %2 %1 bindWith: one with: two with: three one two one
An %1 of embedded text bindWith: example An example of embedded
text
This is a percent sign %%. bindWith: This is a percent sign %
Unused are %1. bindWith: ignored with: unused Unused are ignored
Missing arguments are %2. bindWith: errors Exception occurs

The following example shows the use of message templates to assist in


internationalizing an application. It illustrates how the use of a message template
avoids the language-specific dependencies exhibited by the previous example.
Notice that languages that require the contents of the userName field to appear
after the anOperation field, or different punctuation, can be supported simply by
changing the template string. Normally, the template string would be externalized,
thereby permitting the application to be localized without modification of source
code.
displayWarningFor: userName operation: anOperation
"Display a warning message on the Transcript warning the user called
userName (a String) that the operation called anOperation
(a String) cannot be performed."
| template |
template := 'Sorry, %1, I cannot perform %2!'.
Transcript
cr;
show: (template bindWith: userName with: anOperation).

390 IBM Smalltalk: Programmers Reference


Note: This example is not fully internationalized because it contains a hard-coded
reference to the template 'Sorry, %1 I cannot perform %2!'. Tools and
techniques for removing hard coded references to strings are discussed in
Removing hard-coded strings.

Referencing and initializing localized messages


The process of removing hard-coded references to language-dependent text from
an application is known as string externalization, because the process results in
language-dependent textual information being stored externally to the application.

Language- and culture-dependent strings in an internationalized IBM Smalltalk


application are stored as pool dictionary variables and are accessed directly
(without the need for accessor methods) in application source code. Appropriate
language-dependent strings are bound to language-independent message
identifiers (pool dictionary variables) in response to changes of locale. Thus, the
language independent identifiers are used throughout the application source code
regardless of the target language.

The following example shows the conversion of the previous example to


incorporate an externalized string reference.
displayWarningFor: userName operation: anOperation
"Display a warning message on the Transcript warning the user called
userName that the operation called anOperation cannot be performed."
Transcript
cr;
show:
(MyAppWarningTemplate1 bindWith: Username with: anOperation).

The original template string 'Sorry, %1, I cannot perform %2!' has been replaced
by a reference to a pool variable called MyAppWarningTemplate1. This method is
now language-independent and can be localized simply by modifying the value to
which the pool variable MyAppWarningTemplate1 is bound. Pool variables are most
easily rebound using the features provided by NlsMessageCatalog.

The MyAppWarningTemplate1 pool variable must be reflected in the code that


declares and initializes the pool dictionaries used by the application. It is the
responsibility of each application to declare pool dictionaries and variables,
initialize the values of pool variables (typically using an NlsMessageCatalog), and
remove the pools when the application is unloaded.

Removing hard-coded strings


Literal arrays containing strings require special attention when strings are being
externalized. The manner in which Smalltalk expressions are parsed does not allow
strings in literal arrays to be replaced with pool variables because items in literal
arrays are themselves assumed to be literals.
messageArray
"Answer an Array of language-dependent message strings."

|('message1' 'message2' 'message3' 'message4')

Externalizing the strings contained in the method illustrated previously requires


that the literal array be replaced with an explicitly created array. The externalized
version is presented in the following example. Notice that the string literals have
been replace with pool variables (Message1, Message2, etc.) and that the array
containing the elements is now created explicitly.

Chapter 13. National Language Support 391


messageArray
"Answer an Array of language-dependent message strings."

|Array
with: Message1
with: Message2
with: Message3
with: Message4

Overview of the message localization process


This section illustrates the IBM Smalltalk model by demonstrating how messages
are localized in response to a change in locale and a particular character set in
response to the localize message. The following localization process ensures that the
application contains messages appropriate for the current locale.

For purposes of demonstration we define the class MyNlsEnabledClass that is


controlled by MyNlsEnableApplication as follows. Further assume that the pool
dictionary variables used in MyNlsMessages have initialized (via initialization code)
to contain messages for an English U.S. locale and the iso8859-1 character set.
Object subclass: MyNlsEnabledClass
instanceVariableNames: 'instvar1 instvar2'
classVariableNames: ''
poolDictionaries: 'MyNlsMessages'

Step 1: Replace hard-coded strings with pool dictionary


variables
The programmer has internationalized the MyNlsEnabledApplication and placed the
three messages used by the class (MsgCut, MsgCopy, MsgPaste) into a shared pool
called MyNlsMessages. The initial statelocalized for (english us)of the
MyNlsMessage pool is shown in the following table.
Table 50. MyNlsMessage (localized for (english us))
MsgCopy Copy
MsgCut Cut
MsgPaste Paste

Step 2: Respond appropriately to the localize message


IBM Smalltalk initializes the current locale when the Smalltalk image is started,
and sends the localize message to all loaded applications. The application that
controls MyNlsEnabledClass responds to the localize message as follows:
1. The application queries the current locale and detects that messages should be
updated for a (french france) locale.
2. The application determines which font will be used in the widgets to display
localized messages using methods in Common Graphics. Once the font has
been determined, its coded character set is queried and found to be iso8859-1.
3. The application retrieves messages for #(french france) and coded character
set iso8859-1 from an external file using an NlsMessageCatalog. The
NlsMessageCatalog automatically rebinds the pool dictionary variables in
MyNlsMessages.

Step 3: Ensure that messages are successfully localized


All references to the pool dictionary variables contained in MyNlsMessages now
contain appropriately localized messages for the current locale and the desired
character set. The final state of the MyNlsMessages pool is shown in the following
table.

392 IBM Smalltalk: Programmers Reference


Table 51. MyNlsMessagelocalized for #(french france)
MsgCopy Copier
MsgCut Dcouper
MsgPaste Affichier

Using message catalogs


Typically, interactive applications make extensive use of labels, menus, and help
text, all of which require localization in an international application. An
internationalized application that fetches all localized messages at once and stores
them within the image using pool dictionary variables should not experience any
decrease in user interface response compared to an application that uses
hard-coded message strings. An application might also contain descriptive error
messages that are used only when an condition arises. By maintaining the localized
messages on secondary storage and only loading them as required, the memory
requirements of the application are reduced.

The NlsMessageCatalog has been designed to efficiently support these two


predominant uses of display text in applications through the following two
mechanisms.

External message dictionaries


External message dictionaries are designed for use when all localized messages
are to be retrieved at once and stored in a pool dictionary within Smalltalk.
External message dictionaries are identified by locale and character set, and for this
reason only one message dictionary can be stored in an NlsMessageCatalog for a
given locale and character set.

An external message dictionary is a Dictionary keyed by a message identifier (pool


dictionary variable name) containing localized messages. NlsMessageCatalog
provides protocols specifically tailored for storing and retrieving pool dictionaries
containing localized messages.
Advantage:
High-speed loading for large numbers of messages and fast access time
when individual messages are referenced.
Disadvantage:
Requires more memory because messages are loaded from disk and stored
within the Smalltalk image.

Indexed external messages


Indexed external messages are designed for use when messages are maintained on
secondary storage and loaded individually, as needed. Indexed external messages
are identified by locale, character set, and a numeric message identifier.

An indexed external message is a single, localized message that is identified


numerically and stored for a particular locale and character set. The
NlsMessageCatalog provides protocols specifically tailored for storing and retrieving
indexed messages. Indexed external message identifiers are integers and should be
assigned sequentially starting at 1, because a collection whose size corresponds to
the largest indexed message identifier is maintained on secondary storage. The
careful numbering of messages can reduce the amount of space needed in
secondary storage.

Chapter 13. National Language Support 393


Advantage:
Space savings because localized messages are stored on disk.
Disadvantage:
Slower access time because messages are read from disk individually.

Note: Both indexed external messages and external message dictionaries are
associated with a particular locale and character set. Many indexed external
messages and/or a single external message dictionary can be stored within the
same file for a given locale and character set combination.

Messages externalized using a NlsMessageCatalog (both external message


dictionaries and indexed external messages) typically contain only strings, but can
contain any Smalltalk object that can be dumped by the Swapper without special
dumping options. (Refer to the IBM Smalltalk Users Guide for a complete
description of limitations.)

Although the NlsMessageCatalog is capable of storing Smalltalk objects other than


strings, the developer should be aware that platform resource files cannot store
arbitrary Smalltalk objects. If the message catalog is to be converted to a platform
resource format, it must only contain strings.

Obtaining instances of NlsMessageCatalog


Instances of NlsMessageCatalog are associated with a single disk file.
NlsMessageCatalog instances are created using NlsMessageCatalog>>#on:, which takes
a single String argument that specifies the absolute or relative pathname of the
new or existing message catalog file.
"Create an NlsMessageCatalog using an absolute path."
NlsMessageCatalog on: 'c:\nls\message.cat' "Windows & OS/2
NlsMessageCatalog on: '/usr/nls/messages.cat' "UNIX"

"Create an NlsMessageCatalog in the current directory."


NlsMessageCatalog on: 'messages.cat'.

Using external message dictionaries


Unloading external message dictionaries
The unload:language:territory:characterSet: protocol is used to create (or add to) a
message file that contains a dictionary of messages for a particular locale and
character set. The protocol can also be used to overwrite the value of an existing
external message dictionary. You must intitialize the dictionary being dumped with
the correct keys and values before unloading. Several external message dictionaries
can be stored in a single message catalog file by making repeated calls using
unload:language:territory:characterSet:.

The following example creates an NlsMessageCatalog that contains an external


message dictionary. This example assumes the use of a pool dictionary called
MyNlsMessages containing localized message strings. Return values of all
NlsMessageCatalog operations must be checked to ensure that the operation has
succeeded.
"A code fragment to create a message catalog file containing 'demo.cat'
containing an external message dictionary for the ('english' 'us') locale
and the 'iso8859-1' character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

"Create the message catalog file."

394 IBM Smalltalk: Programmers Reference


(catalog unload: MyNlsMessages
language: 'english'
territory: 'us'
characterSet: 'iso8859-1') ifFalse: [
|self error: 'Unload error: ',catalog currentErrorString].

Loading messages into a pool dictionary


The loadLanguage:territory:characterSet:intoDictionary: protocol is used to update an
existing pool dictionary with values retrieved from a message catalog file. The
NlsMessageCatalog answers a Boolean value indicating success or failure of the load
operation.

The following example loads messages into an existing pool dictionary using an
NlsMessageCatalog. The example assumes the existence of a pool dictionary called
MyNlsMessages into which all messages can be loaded. You must check the return
values of all NlsMessageCatalog operations to ensure that the operation has
succeeded.
"A code fragment to load messages from an existing message catalog file
called 'demo.cat' for the #('english' 'us') locale and the 'iso8859-1'
character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

(catalog loadLanguage: 'english'


territory: 'us'
characterSet: 'iso8859-1'
intoDictionary: MyNlsMessages) ifFalse: [
|self error: 'Load error: ',catalog currentErrorString].

Note: No keys are added to an existing pool by the


loadLanguage:territory:characterSet:intoDictionary: operation. Only the values of
the existing keys are modified. This features ensures that unused shared
pool variables removed during packaging are not reintroduced in
NlsMessageCatalog.

When pool variables are referenced directly in source code, the associations
contained in the pool dictionary are directly referenced by instances of
CompiledMethod. As a result, when a pool dictionary is localized, the associations
that contain the localized messages must be directly modified. Assigning a
localized version of the pool dictionary to the global variable that references the
dictionary does not work. The NlsMessageCatalog contains protocols that directly
modify the associations that contain localized messages and ensure consistency of
loaded messages.

Loading messages without initializing a pool dictionary


The loadLanguage:territory:characterSet: protocol is used to load an external message
dictionary from a message catalog file without loading into a pool dictionary. The
NlsMessageCatalog answers the Dictionary of messages retrieved from disk, or nil if
an error occurs.

The following example shows how to load an external message dictionary without
initializing a pool dictionary. You must check the return values of all
NlsMessageCatalog operations to ensure that the load operation has succeeded.
"A code fragment to load messages from an existing message catalog file
called 'demo.cat' for the #('english' 'us') locale and the 'iso8859-1'
character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

Chapter 13. National Language Support 395


(catalog loadLanguage: 'english'
territory: 'us'
characterSet: 'iso8859-1') isNil ifTrue: [
|self error: 'Load error: ',catalog currentErrorString].

Declaring pool dictionary keys


The declareKeysFor:language:territory:characterSet: message can be used to declare
keys in a pool dictionary from a message catalog file. This operation reads the
external message dictionary for a specified language, territory, and character set
from a message catalog file and declares new keys in a user-supplied pool.

The declareKeysFor:language:territory:characterSet: operation does not affect values of


existing pool variables and does not remove keys from the supplied pool. The
values of newly declared keys are the same as the key to assist in identification of
missing translations. The NlsMessageCatalog answers true if this operation succeeds,
otherwise answers false.

The following example demonstrates how to declare pool keys from a message
catalog file. This example assumes the existence of the pool dictionary
MyNlsMessages in which keys will be declared.
"Declare the pool MyNlsMessages and populate it with keys based on the
messages stored for #('english' 'us' 'iso8859-1')."

| pool catalog |
pool := Smalltalk declarePoolDictionary: #MyNlsMessages.
catalog := NlsMessageCatalog on: 'demo.cat'.

(catalog
declareKeysFor: pool
language: 'english'
territory: 'us'
characterSet: 'iso8859-1') ifFalse: [
|self error: 'Declaration error: ',catalog currentErrorString].

Using declareKeysFor:language:territory:characterSet: can simplify the pool declaration


process, but only if you are using an unmanaged pool dictionary. This method
cannot be used to declare keys in a managed pool.

Defining managed pool dictionaries


You can define managed pool dictionaries using variable declaration methods.
When you do so, tag the pool declaration with a PRAGMA field, which identifies
the pool dictionary as an NLS pool dictionary. The PRAGMA field is a string
prefixed by NLS followed by a space and the name of the catalog (without an
extension).

You should place the following variable declaration in the application that
manages the pool dictionary. The following variable declaration method declares a
pool dictionary called MyNlsMessages, which contains two pool variables (Message1
and Message2) whose catalog file is mynls.cat.
_PRAGMA_MyNlsMessages
"%%PRAGMA DECLARE
(name: MyNlsMessages isPool: true pragma: 'NLS mynls')
(pool: myNlsMessages declarations: (
(name: Message1)
(name: Message2)
))"

396 IBM Smalltalk: Programmers Reference


Deleting external message dictionaries
You can delete an external message dictionary from a message catalog file using
the deleteDictionaryLanguage:territory:characterSet: protocol. This operation removes
only the external message dictionary stored for the specified language, territory,
and character set and does not affect any indexed external messages or other
message dictionaries. The following example demonstrates how to delete an
external message dictionary from a message catalog file.
"Remove the message dictionary stored for #('english' 'us' 'iso8859-1')."
| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.
(catalog
deleteDictionaryLanguage: 'english'
territory: 'us'
characterSet: 'iso8859-1') ifFalse: [
|self error: 'Deletion error: ',catalog currentErrorString].

Note: Deleting an external message dictionary does not immediately decrease the
size of the message catalog file on disk. Deleted external dictionaries are
marked as garbage and their space is reclaimed when the message catalog
file is compressed. See Compression on page 402 for a discussion of the
compression of message catalog files.

Deletion is a permanent operation and cannot be undone. After an external message


dictionary is deleted, it is lost.

Using indexed external messages


When using indexed external messages, you can perform the following operations:
v Unload to create or add messages
v Load to retrieve messages
v Delete to remove messages

Unloading indexed external messages


The unload:language:territory:characterSet:index: protocol is used to create or add an
indexed message to a message catalog file for a particular locale and character set.
This protocol can also be used to overwrite the value of an existing indexed
external message. Several indexed external messages can be stored in a single
message catalog file by making repeated calls using
unload:language:territory:characterSet:index:. The following example illustrates the
process of unloading indexed messages into a message catalog file. Return values
of all NlsMessageCatalog operations must be checked to ensure that the operation
has succeeded.
"A code fragment to create a message catalog file called 'demo.cat'
containing a single indexed external message for the #('english' 'us')
locale and the 'iso8859-1' character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

"Create the message catalog file."


(catalog unload: 'This is an indexed external message'
language: 'english'
territory: 'us'
characterSet: 'iso8859-1'
index: 1) ifFalse: [
|self error: 'Unload error: ',catalog currentErrorString].

Loading indexed external messages


The loadLanguage:territory:characterSet:index: protocol retrieves an indexed external
message from a message catalog file. The NlsMessageCatalog answers the message

Chapter 13. National Language Support 397


retrieved from the catalog, or nil if an error occurs. The following example
illustrates the procedure for loading indexed external messages.
"A code fragment to load indexed external message number 1 from an
existing message catalog file called 'demo.cat' for the #('english' 'us')
locale and the 'iso8859-1' character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

(catalog loadLanguage: 'english'


territory: 'us'
characterSet: 'iso8859-1'
intoDictionary: MyNlsMessages
index: 1) isNil ifTrue: [
|self error: 'Load error: ',catalog currentErrorString].

Deleting indexed external messages


You can delete an indexed external message from a message catalog file using the
deleteIndex:language:territory:characterSet: protocol. This operation removes only the
indexed external message stored for the specified language, territory, character set,
and index and does not affect any other indexed external messages or message
dictionaries. The following example demonstrates how to delete an indexed
external message from a message catalog file.
"Removes the indexed external message number 1 for the
#('english' 'us') locale and the 'iso8859-1' character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

(catalog
deleteIndex: 1
language: 'english'
territory: 'us'
characterSet: 'iso8859-1') ifFalse: [
|self error: 'Deletion error: ',catalog currentErrorString].

Note: Deleting an indexed external message does not immediately decrease the
size of the message catalog file on disk. Deleted indexed external messages
are marked as garbage and their space is reclaimed when the message
catalog file is compressed. See Compression on page 402 for a discussion
of the compression of message catalog files.

Deletion is a permanent operation and cannot be undone. After an external message


dictionary is deleted, it is lost.

Deleting locales from a message catalog file


You can delete an entire locale from a message catalog file using the
deleteLanguage:territory:characterSet:index: protocol. This operation removes both the
indexed external messages and the external message dictionary stored for the
specified language, territory, and character set and does not affect objects stored for
any other locale. The following example demonstrates how to delete a locale from
a message catalog file.
"A code fragment to remove all objects stored for the
#('english' 'us') locale and the 'iso8859-1' character set."

| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.

(catalog
deleteLanguage: 'english'

398 IBM Smalltalk: Programmers Reference


territory: 'us'
characterSet: 'iso8859-1') ifFalse: [
|self error: 'Deletion error: ',catalog currentErrorString].

Note: Deleting a locale does not immediately decrease the size of the message
catalog file on disk. Deleted locales are marked as garbage and their space is
reclaimed when the message catalog file is compressed. See Compression
on page 402 for a discussion of the compression of message catalog files.

Deletion is a permanent operation and cannot be undone. Once deleted, all objects
stored for a locale are lost.

Support for extended locales


It is sometimes desirable for applications to use message catalog files to store
information other than localized messages. To avoid confusing the language,
territory, and character set triples used to store this additional information with
those that contain localized messages the notion of extended locale has been
adopted. An extended locale is defined as a language, territory, character set triple
where the language string begins with the $ character.

Extended locales are intended to be used to identify objects other than localized
messages which are stored in message catalog files. For example, the #($Comment
Comment Comment) extended locale could be used by an application to
maintain a comment about the message catalog file.

The following protocols are provided to determine what language, territory, and
character set combinations are supported by a message catalog file. Each of the
following protocols answers a collection of ordered triples representing the
language, territory, and character sets for which application-defined objects or
localized messages have been stored. The following protocols report errors by
answering nil.
messageIdentifiers
Answers a collection containing language, territory, and character set
triples from which localized messages are stored, excluding objects stored
for extended locales.
hiddenMessageIdentifiers
Answers a collection containing language, territory, and character set
triples which are used to identify extended locales.
allMessageIdentifiers
Answers a collection containing all language, territory, and character set
triples for which messages are stored, including objects stored for extended
locales.

The following example illustrates the use of the preceding protocols:


"A code fragment to determine the locales and character sets
supported by a message catalog file."

| catalog translations extendedLocales allIdentifiers |


catalog := NlsMessageCatalog on: 'demo.cat'.
translations := catalog messageIdentifiers.
extendedLocales := catalog hiddenMessageIdentifiers.
allIdentifiers := catalog allMessageIdentifiers
|Array
with: translations
with: extendedLocales
with: allIdentifiers

Chapter 13. National Language Support 399


As a convenience, the NlsMessageCatalog provides the
supportsLanguage:territory:characterSet: message to determine if a message catalog
file supports a particular language, territory, and character set combination. This
message answers a Boolean value indicating whether or not the requested
combination is supported. The following example demonstrates how to use the
supportsLanguage:territory:characterSet: protocol.
"Testing a message catalog file to determine if messages are stored
for #('english' 'us' 'iso8859-1')."
| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.
|catalog
supportsLanguage: 'english'
territory: 'us'
characterSet: 'iso8859-1'

Note: The messageIdentifiers message is useful for selecting an alternative set of


localized messages if the supportsLanguage:territory:characterSet: message
indicates that no messages are available for the requested language, territory,
and character set.

Compatibilities
NlsMessageCatalog supports compatibilities, a mechanism to facilitate the sharing
of translations among locales. Compatibilities can also be used to describe the
default messages to be used in the event that the message catalog file does not
contain messages for the current locale and character set.

NlsMessageCatalog compatibilities are stored in a sequenceable collection (array) of


arrays. The first element of each array describes the primary language, territory,
and character set (locale triple) for which localized message strings are actually
stored in the catalog file. The remaining elements of each array describe locales
which are compatible with, and can use the messages of, the primary locale. A
catalog file contains one set of compatibilities.
| "Structure of NlsMessage Catalog Compatibilities"
| #(
| #(
| #('japanese' 'japan' 'ibm-932')
| #('japanese' 'japanese' 'japan' 'microsoft-shiftjis')
| )
| #(
| #('german' 'germany' 'iso8859-1')
| #('german' '*' 'iso8859-1')
| )
| #(
| #('english' 'us' 'ansi-ascii')
| #('*' '*' '*')
| )
| )

| NlsMessageCatalog>>localeCompatibleWithLanguage:territory:characterSet: can be used


to determine the locale whose messages are compatible with a given language,
territory, and character set. The compatibilities depicted in the example above
indicate that the catalog contains messages for primary locales
japanese-japan-ibm-932, german-germany-iso8859-1, and english-us-ansi-ascii.

Given a locale triple, localeCompatibleWithLanguage:territory:characterSet: first


determines if messages are explicitly stored for that locale. In the above example,
the locale japanese-japan-ibm-932 is actually stored in the catalog. Therefore,
localeCompatibleWithLanguage:territory:characterSet uses the messages of this locale

400 IBM Smalltalk: Programmers Reference


without consulting the compatibility information. Similarly, locales
german-germany-iso8859-1 and english-us-ansi-ascii use their stored messages.

If messages for the given locale are not explicitly stored in the catalog,
localeCompatibleWithLanguage:territory:characterSet: uses the compatibility information
to determine the compatible locale. The process starts with the first array (for
example, (japanese-japan-ibm-932 japanese-japan-microsoft-shiftjis) in the previous
example) and proceeds sequentially until the last array (for example,
(english-us-ansi-ascii *-*-*) in the previous example). If an element of an array
matches the given locale the messages of the primary locale (for example,
japanese-japan-ibm-932) are considered to be compatible with the given locale.
Compatibility matching stops after the first match is found.

The last compatibility illustrated in the previous example, #(english-us-ansi-ascii


*-*-*), indicates that if no other array was able to match the given locale triple,
use *-*-* to match the locale triple, and default to using english-us-ansi-ascii
messages. ANSI-ASCII characters are contained in the lower portion of many
character sets and thus provide a reasonable fall-back.
"Sharing localized messages using compatibilities"

german-switzerland-iso8859-1

matches
#('german' 'germany' 'iso8859-1')
#('german' '*' 'iso8859-1')

and uses
'german' 'germany' 'iso8859-1'.

The next example


german-switzerland-ibm850

does not match


'german' '*' 'iso8859-1'

and therefore uses messages for


'english' 'us' 'ansi-ascii'.

The next example


japanese-japan-microsoft-shiftjis

matches
#('japanese' 'japan' 'ibm-932')
#('japanese' 'japan' 'microsoft-shiftjis')

and uses messages for


'japanese' 'japan' 'ibm-932'.

The compatibilities from the example suggest that the application has iso8859-1
translations for German, but likely has has not been localized for different
German-speaking territories. In other words, the application likely has a single
German version sold in multiple German-speaking territories. The example above
illustrates the results of the compatibilities matching process as applied to the
compatibilities described in the example showing the structure of
NlsMessageCatalog compatibilities. The locale english-us-ansi-ascii is an abstract

Chapter 13. National Language Support 401


locale. Abstract locales do not correspond to actual platform supported locates;
however, they can be used to help organize the messages of a catalog.

Storing and retrieving compatibilities


NlsMessageCatalog provides the compatibilities and compatibilities: methods to retrieve
and store message catalog compatibilities. The following example illustrates the
process of storing and retrieving message catalog compatibilities.
| "Set and retrieve the compatibilities of a message catalog file."
| | catalog compatibilities |
| compatibilities :=
| #(
| #(
| #('japanese' 'japan' 'ibm-932')
| #('japanese' 'japanese' 'japan' 'microsoft-shiftjis')
| )
| #(
| #('german' 'germany' 'iso8859-1')
| #('german' '*' 'iso8859-1')
| )
| #(
| #('english' 'us' 'ansi-ascii')
| #('*' '*' '*')
| )
| )
| catalog := NlsMessageCatalog on: 'demo.cat'.
| (catalog compatibilities: compatibilities) ifFalse: [
| |self error: 'Unload error: ',catalog currentErrorString].
|
| |catalog compatibilities

Compression
Deletion of objects from a message catalog file is accomplished by marking objects
as unreachable and does not require the message catalog file to be entirely
rewritten. Furthermore, the structure of NlsMessageCatalog files allocates extra space
to objects stored with the file to allow efficient object modification. For these
reasons a compression phase is necessary to reclaim space used by unreachable
objects and to remove extra space allocated to live objects.

The compressMinimal: method is used to compress a message catalog file. This


method accepts a Boolean argument that indicates whether the file should be
compressed to its smallest possible size (at the expense of object modification
efficiency). If true is passed as a parameter to compressMinimal: then all space used
by unreachable objects is reclaimed and extra space allocated to live objects is also
removed. If false is passed, all space used by unreachable objects is reclaimed, but
extra space allocated to live objects remains.
"Compress the message catalog file 'demo.cat' first leaving extra
space for future growth, then to its minimum size."
| catalog |
catalog := NlsMessageCatalog on: 'demo.cat'.
(catalog compressMinimal: false) ifFalse: [
|self error: 'Compression error: ',catalog currentErrorString].
(catalog compressMinimal: true) ifFalse: [
|self error: 'Compression error: ',catalog currentErrorString].

Note: When compressing message catalog files during the development cycle, it is
desirable to use compressMinimal: false option to ensure that updating
message catalog files is as efficient as possible. When all modifications have
been done, you can use the compressMinimal: true option to minimize the
size of the message catalog file.

402 IBM Smalltalk: Programmers Reference


Limitations
NlsMessageCatalogs can contain only objects that can be dumped by the Swapper
without special dumping options. (Refer to the IBM Smalltalk Users Guide .)

Error detection and description


Because NlsMessageCatalog is intended for use at runtime and debugging facilities
might not be available, error codes are employed to provide an error tracing
mechanism. The value of the current error code should be examined after each
operation to determine if an error occurred during the last operation. Alternatively,
the hasErrorOccurred message can be used to check if an error occurred during the
last operation.

List of error codes


The following list includes all error codes and their corresponding messages. Each
error code is associated with a descriptive error string and an error object, which is
typically related to the current error or the cause of the current error. This error
object can be obtained by sending the currentErrorObject message to an instance of
NlsMessageCatalog. Similarly, the descriptive error string can be obtained by
sending the currentErrorString to an instance of NlsMessageCatalog. The
interpretation of the error object for each error is provided below.
0 Error string: No Errors.
Error object: nil
1000 Error string: Locale not supported.
Error object: Locale name for which messages could not be found.
1002 Error string: The pool being initialized contains keys for which no
translations exist.
Error object: A set of the keys missing translations.
1003 Error string: Could not open specified file.
Error object: The pathname of the file that could not be opened.
1004 Error string: Non-portable messages detected.
Error object: The message strings that are not portable.
1005 Error string: Error renaming file. Ensure file name is valid and no old
working files exist.

Error object:
Array
with: name of file being renamed
with: name of rename target
with: CfsError resulting from failed attempt to rename file.
1006 Error string: Error removing secondary working file.

Error object:
Array
with: name of secondary working file
with: CfsError resulting from failed attempt to rename file.
1007 Error string: Invalid attempt to crossload.
Error object: An instance of NlsMessageCatalogHeader.
1010 Error string: File write error.

Chapter 13. National Language Support 403


Error object:
Array
with: an instance of NlsMessageCatalogHeader
with: number of bytes that should have been written
with: number of bytes actually written
1011 Error string: FatalError: Object size estimation failedworking file is
corrupt.
Error object: Object for which size estimation failed.
1012 Error string: Catalog file is corrupt (or not a catalog file).
Error object: An instance of NlsMessageCatalogHeader.
1013 Error string: Invalid indexed message identifier (must be >0).
Error object: The invalid indexed message identifier.
1014 Error string: Indexed message not found.

Error object:
Array
with: Locale name for which message could not be found.
with: Index of message which could not be found.
1015 Error string: No messages stored for locale.
Error object: Locale name for which messages could not be found.
1016 Error string: No message dictionary available.
Error object: Locale name for which messages could not be found.
1017 Error string: Locale not found.
Error object: Locale name for which messages could not be found.
2000 Error string: Error encountered loading object.
Error object: An instance of ObjectLoader.
2001 Error string: Error encountered dumping object.
Error object: An instance of ObjectDumper.

Support for packaging


The NlsMessageCatalog function described in the previous sections is partitioned
between the applications NlsExternalizationRuntime and NlsExternalizationTools to
support packaging.

NlsExternalizationRuntime provides the necessary class definitions for


NlsMessageCatalog, its support classes, and all protocols required for reading and
querying message catalog files. NlsExternalizationTools defines extensions to the
basic NlsMessageCatalog and its support classes to enable creation of message
catalog files. If the application being packaged has no requirement to create
message catalog files at runtime, only the NlsExternalizationRuntime application
should be included in the packaged image.

The NlsExternalizationTools application also defines the RCConverter class that assists
in converting localized message dictionaries to Microsoft Windows and OS/2
resource file formats. The RCConverter class is intended primarily for use in a
development image and should most likely be excluded from a runtime image.

404 IBM Smalltalk: Programmers Reference


Support for platform resource formats
The subclasses of NlsPlatformResourceConverter are provided to enable pool
dictionaries containing localized messages to be stored in and retrieved from
platform resource file formats.

RCConverter
Dynamic link libraries (.DLL files) created using the Microsoft and IBM OS/2
resource compilers are supported. The class RCConverter can produce a resource
compiler input file from a pool dictionary, and can re-create the pool dictionary
from the input file and the dynamic link library it produces.

Structure of the resource compiler input file


The first portion of the resource compiler input file generated by the RCConverter is
a comment that identifies the file along with the date and time when it was
created.

Immediately following the first comment is a series of define statements that


describe the pool dictionary from which the file was created. Localized messages
stored as IBM Smalltalk pool variables are identified by string keys in a pool
dictionary; however, when these messages are stored in dynamic link libraries they
must be identified by ordinal constants. The conversion of string identifiers to
ordinal values is accomplished by associating each string identifier with an ordinal
value using a define statement in the resource compiler input file. For example:
define <aPoolDictionaryKey> <anInteger>

Localized messages and their associated comments follow the series of define
statements. Localized messages are represented as entries in a STRINGTABLE
statement associated with an ordinal value declared in the series of define
statements.

Note: The resource file representation of a string can be quite different from its
Smalltalk appearance. Many non-printing and special characters must be
converted into the \DDD format for use with the resource compiler. When
retrieved from a dynamic link library using an RCConverter or a
PlatformFunction the string is equivalent to the original Smalltalk string.

Creating resource compiler input files


RCConverter provides the convertMessages:toResourceFile: and
convertMessages:comments:toResourceFile: methods to convert a dictionary containing
localized messages into a resource compiler input file. The dictionary containing
localized messages must be keyed by string identifiers, and can contain only
messages conforming to the conditions described in RCConverter limitations on
page 407. An attempt to convert a dictionary containing messages that are not
valid generates an error.

In addition to a file name and a dictionary containing localized messages, the


convertMessages:comments:toResourceFile: method accepts a dictionary containing
comments to be written into the resource compiler input file. The dictionary
containing comments should have the same keys as the dictionary that contains
localized messages. The comment dictionary must conform to the same limitations
that apply to message dictionaries, but need not contain a comment for every
message in the dictionary being converted.

The following code example creates a resource compiler input file:

Chapter 13. National Language Support 405


"Create a resource compiler input file."
| dict comments |
dict :=
Dictionary new
at: 'X' put: 'localized message X';
at: 'Y' put: 'localized message Y';
at: 'Z' put: 'localized message Z';
yourself.
comments :=
Dictionary new
at: 'X' put: 'comment X';
at: 'Y' put: 'comment Y';
yourself.

"Store the new pool dictionary in Smalltalk."


Smalltalk at: #DemoPool put: dict.

RCConverter new
convertMessages: (Smalltalk at: #DemoPool)
comments: comments
toResourceFile: 'demo.rc'

The preceding example illustrates how dictionaries containing localized messages


and comments are converted into a resource compiler input file. The example
produces the resource compiler input file called demo.rc, whose contents are
presented in the following example:
/* Resource File for: #DemoPool.
This file is generated automatically for use as the input file for the
Microsoft Resource Compiler. When this file was constructed Smalltalk
message identifiers were converted into numeric identifiers stored as
a group of #define statements within this file.
This file is: demo.rc
Creation date: 14.03.94 at: 14,37,12

This file was created with the following Smalltalk locale settings:

Language: german
Territory: switzerland
Code page/Language driver: deu
*/
#define X 0
#define Z 1
#define Y 2

STRINGTABLE
BEGIN
0, "localized message X" /* comment X */
1, "localized message Z"
2, "localized message Y" /* comment Y */
END

Reading from platform resource files


RCConverter provides the indexFrom: and messagesFromDLL:index: methods to
rebuild a dictionary containing localized messages from messages stored in a
dynamic link library (DLL).

Dynamic link libraries use integers to identify localized messages and IBM
Smalltalk pool dictionaries use string identifiers. You must provide the mapping
between string identifiers to be used in the reconstructed dictionary and the
integer identifier used by the dynamic link library. The indexFrom: method can be
used to reconstruct an index from a resource compiler input file based upon a

406 IBM Smalltalk: Programmers Reference


series of define statements that bind string identifiers to integer values. The
following example demonstrates the process of rebuilding an index from an
existing resource compiler input file:
"Retrieve an index by parsing a resource compiler input file."
|RCConverter new indexFrom: 'demo.rc'

The messagesFromDLL:index: protocol answers a reconstructed dictionary of


localized messages based upon the contents of a dynamic link library and an index
provided by the developer. messagesFromDLL:index: accepts any dictionary that
provides mappings between strings and integers as its index parameter, although
the indexFrom: method is the recommended mechanism for generating the index.
The messagesFromDLL:index: method does not require a resource compiler input file
to function. The following example illustrates the reconstruction of a dictionary
containing localized messages from a dynamic link library:
"Answer a dictionary reconstructed from a DLL."
| converter index |
converter := RCConverter new.
index := converter indexFrom: 'demo.rc'.
|converter messagesFromDLL: 'demo.dll' index: index.

RCConverter limitations
Messages must be instances of String or DBString containing Characters whose
values are strictly greater than zero (1 to 65535).

Note: The resource file representation of a single Character can range from 1 to 4
bytes. Characters expanded in this manner are the special or nonprinting
characters whose values are 1 through 31, 34, and 127. For example,
(Character value: 7) has a resource file representation of: \007.

RCConverter error codes


All errors codes and their corresponding messages are detailed below. Each error
code is associated with a descriptive error string and an error object, which is
typically related to or the cause of the current error. This error object can be
obtained by sending the message currentErrorObject to an instance of RCConverter.
Similarly, the descriptive error string can be obtained by sending the message
currentErrorString to an instance of RCConverter. The interpretation of the error
object for each error follows.
Error code
Message
2 Error string: Message names cannot contain double byte characters.
Error object: The invalid message name.
3 Error string: Message cannot contain characters of value < 2.
Error object: The invalid message text.
4 Error string: Some of the messages contain non-string objects.
Error object: The invalid message name.
5 Error string: Some of the messages contain empty strings. Messages cannot
be empty strings.
Error object: The invalid message text.
6 Error string: Invalid message name the phrase _part_ is a reserved token.
Error object: The invalid message name.
7 Error string: File Error: Could not open specified file.

Chapter 13. National Language Support 407


Error object: File name (String) of the file that could not be opened.
8 Error string: Could not open DLL
Error object: File name (String) of the DLL that could not be opened.
9 Error string: Could read messages from DLL.
Error object: The integer identifier of the unreadable message.
10 Error string: Could not close resource DLL.
Error object: The module handle that could not be closed.
11 Error string: Could not read index from resource file DLL.
Error object: File name (String) of the invalid resource file.

Localization support API


The following classes provide protocols that directly support the
internationalization and localization of software.
v NlsMessageCatalog
v RCConverter

NlsMessageCatalog class methods


on: Answers a new, initialized instance of the receiver on the file specified by
the absolute or relative path aPathName (String).

NlsMessageCatalog instance methods


allMessageIdentifiers
Determines which locales are supported by localized messages stored in
the file specified by the receivers path. Answers a collection of all locale
names for which the catalog file specified by path has mappings including
locale names starting with the $ character whose names are not returned
by the messageIdentifiers protocol. Answers nil if an error occurs.
compatibilities
Answers an array of compatibility arrays, nil if no compatibility arrays can
be located. Each compatibility array has the following form:
#(
<primary local triple> "Triple for which messages are
actually stored."
<compatible pattern> "Triple for locale compatible with primary."
...
(compatible pattern> "Triple for local compatible with primary."
)
compatibilities:
Unloads anArrayOfcompatibilityArrays into the file represented by the
receiver. The format of the argument anArrayOfcompatibilityArrays is
described in the compatibilities method comment. Answers true on success,
otherwise answers false.
compressMinimal:
Compresses the file represented by the receiver removing any dead areas.
If compressToMinimumSize is true, the file is made its smallest possible size.
If compressToMinimumSize is false, padding is included in the new file so
that future writes might not need to grow the file. Answers true if
successful, otherwise answers false

408 IBM Smalltalk: Programmers Reference


currentError
Answers the integer error code describing the last error that occurred.
Answers 0 if no errors have occurred.
currentErrorObject
Answers the object(s) that caused the last error that occurred. This can be a
single object or an array of objects. Refer to List of error codes on
page 403.
currentErrorString
Answers a string of the last error that occurred. This string offers an
explanation of the last error, and appends extended error information from
the Swapper, if appropriate.
declareKeysFor:language:territory:characterSet:
Loads the localized messages stored in the receiver at the key specified by
the language, territory, and character set (strings). Declares keys in the
specified pool dictionary if they are missing. The values of newly declared
keys are the same as the key to allow identification of missing translations.
Answers true on success, otherwise answers false.
deleteDictionaryLanguage:territory:characterSet:
Deletes the external message dictionary for the locale specified by the
language, territory, and character set (strings) from the catalog file specified
by the receivers file name. Answers true if the external dictionary message
is successfully removed, otherwise answers false.
deleteIndexLanguage:territory:characterSet:
Deletes the indexed message specified by the index for the locale specified
by the language, territory, and character set (strings) from the catalog file
specified by the receivers file name. Answers true if the localized message
was successfully removed, otherwise answers false.
deleteLanguage:territory:characterSet:
Deletes all messages for the locale specified by the language, territory, and
character set (strings) from the catalog file specified by the receivers file
name. Answers true if the messages are successfully removed, otherwise
answers false.
fileName
Answers a String that is the fileName of the message catalog file
represented by the receiver. The fileName is the string passed as an
parameter to the on: method.
fileName:
Sets the fileName of the message catalog file represented by the receiver to
the specified string.
hasErrorOccurred
Answers true if an error has occurred in the receiver, otherwise answers
false.
hiddenMessageIdentifiers
Determines which locales are supported by localized messages stored in
the file specified by the receivers path. Answers a collection of the
supported locale names, in which the locale names start with a $
character. These names are not returned by the messageIdentifiers protocol.
Answers nil if an error occurs.
indexesForLanguage:territory:characterSet:
Determines which indexed message identifiers are available for the locale

Chapter 13. National Language Support 409


specified by the language, territory, and character set (strings). Answers a
collection of indexed message identifiers (positive integers) for which the
message catalog file specified by the receivers file name has mappings.
Answers nil if an error occurs.
loadLanguage:territory:characterSet:
Answers the dictionary of localized messages specified by the language,
territory, and character set stored in the file specified by the receivers path.
Answers the dictionary of retrieved messages if successful, otherwise
answers nil.
loadLanguage:territory:characterSet:index:
Answers the indexed message specified by the positive integer index for
the locale and character set specified by language, territory, and character
set, contained in the file specified by path. Answers the localized message
retrieved from disk if successful, answers nil otherwise.
loadLanguage:territory:characterSet:intoDictionary:
Localizes the specified EsPoolDictionary using message for the locale
specified by the language, territory, and character set (strings). Modifies the
association in the pool dictionary with localized messages contained in the
file specified by path. Answers true if successful, otherwise answers false.

Note: Only keys already in the pool have their values set.
localeCompatibleWithLanguage:territory:characterSet:
Retrieves and examines the receivers compatibilities array and determines
which message identifier best matches the language, territory, and
character set.
If the message catalog file contains messages for the specified language,
territory, and character set, the compatibilities are not examined and an
array containing the specified the language, territory, and character set is
returned. Otherwise the matching process examines each line of the
compatibilities array (whose format is described in the compatibilities
method comment). Within a line of the compatibilities, array patterns are
examined from left to right. If no match is found on a given line, the
search continues to the following lines until a match is found or no more
lines are available. A match occurs if the three elements of the pattern
array match language, territory, and character set.
Answers a three-element array consisting of strings that identify the
language, territory, and character set. Answers nil if an error occurs or if no
compatibility matches the supplied arguments.
messageIdentifiers
Determines which locales are supported by localized messages stored in
the file specified by the receivers path. Answers a collection of the locale
names for which the message catalog file specified by path has mappings.
Answers nil if an error occurs.
objectPlatform
Answers the object platform from which the message catalog file specified
by the receivers path was dumped. Answers a string indicating the object
platform, or nil if an error occurs.
pathName
Answers a string that contains the full path name of the message catalog
file represented by the receiver (nil if the full path has not yet been
computed).

410 IBM Smalltalk: Programmers Reference


supportsLanguage:territory:characterSet:
Determines whether the specified file contains localized messages for the
locale specified by the strings language, territory, and character set.
Answers true if the locale is supported, false if unsupported, or nil if an
error occurs.
unload:language:territory:characterSet:
Stores the localized messages contained in the specified pool dictionary for
the locale defined by the language, territory, and character set in the file
specified by the receivers fileName. Answers true if successful, otherwise
answers false.
unload:language:territory:characterSet:index:
Stores the given localized message as an indexed message in a file
specified by the receivers fileName, for the locale defined by the language,
territory, and character set, and the positive integer index. Answers true if
successful, otherwise answers false.

RCConverter class methods


new Answers a new, initialized instance of the receiver.

RCConverter class methods instance methods


convertMessages:comments:toResourceFile:
Creates a file resourcePath that can be used as input to the Microsoft or IBM
OS/2 resource compilers. This method converts the contents of the
specified pool dictionary into numbered messages in the input file, storing
the mapping as a series of define statements, and annotates this input file
with comments contained in the specified comment dictionary. Answers
true if successful, otherwise answers false.
convertMessages:toResourceFile:
Creates a file resourcePath that can be used as input to the Microsoft or IBM
OS/2 resource compilers. This method converts the contents of the
specified pool dictionary into numbered messages in the input file storing
the mapping as a series of define statements. Answers true if successful,
otherwise answers false.
indexFrom:
Parses the specified input file of the Microsoft or IBM OS/2 resource
compiler and answers a dictionary that contains the pool key-numeric
mappings stored in the define statements of the input file. Answers nil if an
error occurs.
messagesFromDLL:index:
Answers the dictionary rebuilt from the specified dynamic link library
using the mapping between dictionary keys and numeric identifiers
contained in the index. Answers the rebuilt dictionary if successful, nil if
an error occurs.

IBM Smalltalk locale names


Table 52. Supported language and territory combinations
Language Territory Language Territory
arabic arabic hungarian hungary
croatian yugoCroatian icelandic iceland
czech czechoslovakia italian italy

Chapter 13. National Language Support 411


Table 52. Supported language and territory combinations (continued)
Language Territory Language Territory
danish denmark italian switzerland
dutch belgium japanese japan
dutch netherlands korean korea
english australia lappish lapland
english britain lettish latvia
english canada lithuanian lithuania
english ireland maltese malta
english new zealand norwegian norway (Bokmal)
english us norwegian norway (Nynorsk)
estonian estonia polish poland
faeroese faeroeIsl portuguese brazil
farsi iran portuguese portugal
finnish finland russian russia
french belgium simpChinese china
french canada slovak czechoslovakia
french france spanish mexico
french switzerland spanish spain (Mod)
german austrian spanish spain (Trad)
german germany swedish sweden
german switzerland thai thailand
greek greece tradChinese taiwan
hebrew israel turkish turkey
hindi india urdu pakistan

Manual localization
Methods are provided to access and change the state information of LCCollate,
LCMonetary, LCMessages, LCNumeric, LCTime, and Locale objects. In particular, the
following methods are provided to enable you to perform further localization by
manually setting the values of various fields. This can be used to override values
obtained from the platform, or to configure locale-specific values on platforms that
do not provide the information.

Note: Some platforms do not have a one-to-one match with all of the formatting
information maintained by LCCollate, LCCType, LCMonetary, LCMessages,
LCNumeric, LCTime, and Locale. To prevent an error, a universal policy forces
initialization of such variables to U.S. English. A description of values that
cannot be obtained from the platform, and their default values, is presented
in Locales and platform representation on page 415.

LCCollate
collateTable:
Sets the collateTable. The collate table is a ByteArray consisting of 256
positions. The value at each position is the collating weight of a given

412 IBM Smalltalk: Programmers Reference


character. The character with the highest collating weight appears first in
an alphabetical sort. Information for the character with value 0 is stored at
position 1, and so on.

LCMessages
dataSeparator:
Sets the data separator string explicitly.
noStr: Sets the localized versions of the string No.
yesStr: Sets the localized versions of the string Yes.

LCMonetary
currencySymbol:
Sets the currency symbol used when printing a formatted monetary
quantity.
fracDigits:
Sets the number of fractional digits that appear in a formatted numeric
quantity.
intCurrSymbol:
Sets the currency symbol used when printing an international monetary
quantity.
intFracDigits:
Sets the number of fractional digits that appear in a formatted monetary
quantity.
monDecimalPoint:
Sets the decimal point used when formatting a monetary quantity.
monGrouping:
Sets the manner in which digits are grouped in a formatted monetary
quantity.
monThousandsSep:
Sets the thousands separator used in a formatted monetary quantity.
nCsPrecedes:
Combined with the following two methods, controls the formatting of
negative monetary quantities.
nSepBySpace:
Combined with the preceding and following methods, controls the
formatting of negative monetary quantities.
nSignPosn:
Combined with the preceding two methods, controls the formatting of
negative monetary quantities.
negativeSign:
Sets the indicator used to indicate a negative quantity in a formatted
monetary quantity.
positiveSign:
Sets the indicator used to indicate a positive quantity in a formatted
monetary quantity.

Chapter 13. National Language Support 413


pCsPrecedes:
Combined with the following two methods, controls the formatting of
non-negative monetary quantities.
pSepBySpace:
Combined with the preceding and following methods, controls the
formatting of non-negative monetary quantities.
pSignPosn:
Combined with the preceding two methods, controls the formatting of
non-negative monetary quantities.

LCNumeric
decimalPoint:
Sets the decimal point used when formatting a numeric quantity.
grouping:
Sets the manner in which digits are grouped in a formatted numeric
quantity.
thousandsSep:
Sets the thousands separator used in a formatted numeric quantity.

LCTime
abDay: Sets the abbreviated days names.
abMon:
Sets the abbreviated month names.
amPm: Sets the two-element array used to represent the ante-meridian (before
noon) and post-meridian (after noon) indicator strings. Element1 is AM;
Element2 is PM.
day: Sets the full day names.
dFmt: Sets the date format template for the printing of dates.
dtFmt: Sets the date-time format template for the printing of dates and times.
mon: Sets the month names.
tFmt: Sets the time format template for the printing of times.

Locale
countryAbbreviation:
Sets the receivers two-character ISO country abbreviation string.
language:
Sets the descriptive language string for the receivers locale.
territory:
Sets the descriptive territory string for the receivers locale.

414 IBM Smalltalk: Programmers Reference


Locales and platform representation
The concept of a locale defined by language and territory provides a
platform-independent method of specifying international language-dependent
settings. NLS functions translate the IBM Smalltalk locale names to platform values
as illustrated in the following figure:

The following sections detail the mapping from IBM Smalltalk locale definitions to
platform values and discuss items of interest for each supported platform.
Although you normally do not need to map locale descriptions onto platform
values, this section illustrates the source of some platform limitations.

Note: When platforms do not have a one-to-one match with all of the formatting
information maintained by LCCollate, LCCType, LCMonetary, LCMessages,
LCNumeric, LCTime, and Locale, these methods are initialized with English
U.S. values. Manual localization on page 412 describes methods that you
can use to override values obtained from the platform, or to configure
locale-specific values on platforms that do not provide the information.

OS/2 Presentation Manager


As illustrated in the figure above, each IBM Smalltalk locale name corresponds to
an ordered pair of integers representing an OS/2 country code and OS/2 code
page. Table 54 on page 416 defines the mappings from IBM Smalltalk locale
definitions to country code and code page pairs. Locales that are not directly
supported by the operating system are marked with a horizontal bar and are not
available on OS/2.

For OS/2 Presentation Manager, the platform locale information is a combination


of values retrieved from the operating system and the country control panel
settings. When the platform default locale is requested (for example, LCMonetary
for: #(' ')), values are taken from the country control panel settings. When
information for a specific locale is requested (for example, LCMonetary for:
#('english' 'us')), the default values for that locale are obtained from the
operating system, even if the locale requested is the same as the platform default
locale.

When information is unavailable from the platform, U.S. English default values are
provided. The following table shows the values that are not available from the
operating system, along with their default values.

Note: The country control panel setting for monetary leading zero is not
supported and is ignored on OS/2.

Chapter 13. National Language Support 415


Table 53. Items not provided by OS/2 and their default values
Class Item IBM Smalltalk default value
LCMessages noStr No
yesStr Yes
LCMonetary intCurrSymbols Set to same operating system-supplied
value as currencySymbol
intFracDigits Set to same operating system-supplied
value as fracDigits
monGrouping 3 0
monThousandsSep Set to operating system thousands
separator
negativeSign -
positiveSign
LCNumeric decimalPoint Set to same operating system-supplied
value as monDecimalPoint
grouping 3 0
LCTime abDay #(Sun Mon Tues Wed Thu Fri
Sat)
abMon #(Jan Feb Mar Apr May Jun
Jul Aug Sep Oct Nov Dec)
day #(Sunday Monday Tueday
Wednesday Thusday Friday
Saturday)
mon #(January February March April
May June July August September
October November December)

Use of the country control panel


Changing the current country using the country control panel does not change the
platform default locale. The platform default locale is defined by the config.sys
file and can be modified using the OS/2 selective install.
Table 54. Map from locale names to OS/2 platform values
Locale langauge Locale territory Country Code pages
code
arabic arabic 785 864, 437 850
croatian yugoCroatian 38 852, 850
czech czechoslovakia 42 852, 850
danish denmark 45 865, 850
dutch belgium 32 437, 850
dutch netherlands 31 437, 850
english australia 61 437, 850
english britain 44 437, 850
english canada
english ireland
english new zealand
english us 1 437, 850

416 IBM Smalltalk: Programmers Reference


Table 54. Map from locale names to OS/2 platform values (continued)
Locale langauge Locale territory Country Code pages
code
estonian estonia
faeoroese faeroelsl
farsi iran
finnish finland 358 437, 850
french belgium 32 437, 850
french canada 2 863, 850
french france 33 437, 850
french switzerland 41 437, 850
german austria
german germany 49 437, 850
german switzerland 41 437, 850
greek greece
hebrew israel 972 862, 437, 850
hindi india
hungarian hungary 36 852, 850
icelandic iceland 354 850, 861
italian italy 39 437, 850
italian switzerland 41 437, 850
japanese japan 81 932, 942, 437, 850
korean korea 82 949, 944, 437, 850
lappish lapland
lettish latvia
lithuanian lithuania
maltese malta
norwegian norway (Bokmal)
norwegian norway (Nynorsk) 47 865, 850
polish poland 48 852, 850
portuguese brazil
portuguese portugal 351 860, 850
russian russia
simpChinese china 86 936, 946, 437, 850
slovak czechoslovakia 42 852, 850
spanish mexico 3 437, 850
spanish spain (Mod) 34 437, 850
spanish spain (Trad) 34 437, 850
swedish sweden 46 437, 850
thai thailand
tradChinese taiwan 88 938, 948, 437, 850
turkish turkey 90 857, 850

Chapter 13. National Language Support 417


Table 54. Map from locale names to OS/2 platform values (continued)
Locale langauge Locale territory Country Code pages
code
urdu pakistan

Microsoft Windows
As illustrated in the diagram at the beginning of this section, each IBM Smalltalk
locale name maps directly to a pair of language and sublanguage identifier
constants. The following table defines the mappings from IBM Smalltalk locale
definitions to language and sublanguage identifier pairs. The language and
sublanguage constants are defined in the WINNT.H header file that is part of the
Windows NT software development kit.
Table 55. Windows platform values and IBM smalltalk locale names
IBM Smalltalk Locale Name Platform Mapping
Language Territory Language ID Sublanguage ID
arabic arabic
croatian yugoCroatian
czech czechoslovakia LANG_CZECH SUBLANG_DEFAULT
danish denmark LANG_DANISH SUBLANG_DEFAULT
dutch belgium LANG_DUTCH SUBLANG_DUTCH_BELGIAN
dutch netherlands LANG_DUTCH SUBLANG_DUTCH
english australia LANG_ENGLISH SUBLANG_ENGLISH_AUS
english britain LANG_ENGLISH SUBLANG_ENGLISH_UK
english canada LANG_ENGLISH SUBLANG_ENGLISH_CAN
english ireland LANG_ENGLISH SUBLANG_ENGLISH_EIRE
english new zealand LANG_ENGLISH SUBLANG_ENGLISH_nz
english us LANG_ENGLISH SUBLANG_ENGLISH_US
estonian estonia
faeroese faeroeIsl
farsi iran
finnish finland LANG_FINNISH SUBLANG_DEFAULT
french belgium LANG_FRENCH SUBLANG_FRENCH_BELGIAN
french canada LANG_FRENCH SUBLANG_FRENCH_CAN- ADIAN
french france LANG_RENCH SUBLANG_FRENCH
french switzerland LANG_FRENCH SUBLANG_FRENCH_SWISS
german austria LANG_GERMAN SUBLANG_GERMAN_AUS- TRIAN
german germany LANG_GERMAN SUBLANG_GERMAN
german switzerland LANG_GERMAN SUBLANG_GERMAN_SWISS
greek greece LANG_GREEK SUBLANG_DEFAULT
hebrew israel
hindi india
hungarian hungary LANG_HUNGARIAN SUBLANG_DEFAULT
icelandic iceland LANG_ICELANDIC SUBLANG_DEFAULT

418 IBM Smalltalk: Programmers Reference


Table 55. Windows platform values and IBM smalltalk locale names (continued)
IBM Smalltalk Locale Name Platform Mapping
italian italy LANG_ITALIAN SUBLANG_ITALIAN
italian switzerland LANG_ITALIAN SUBLANG_ITALIAN_SWISS
japanese japan LANG_JAPANESE SUBLANG_DEFAULT
korean korea LANG_KOREAN SUBLANG_DEFAULT
lappish lapland
lettish latvia
lithuanian lithuania
maltese malta
norwegian norway (Bokmal) LANG_NORWEGIAN SUBLANG_NORWEGIAN_BOK-
MAL
norwegian norway (Nynorsk) LANG_NORWEGIAN SUBLANG_NORWEGIAN_NY-
NORSK
polish poland LANG_POLISH SUBLANG_DEFAULT
portuguese brazil LANG_PORTUGUESE SUBLANG_PORTUGUESE
portuguese portugal LANG_PORTUGUESE SUBLANG_PORTUGUESE_BRA-
ZILIAN
russian russia LANG_RUSSIAN SUBLANG_DEFAULT
simpChinese china LANG_CHINESE SUBLANG_CHINESE_SIMPLI- FIED
slovak czechoslovakia LANG_SLOVAK SUBLANG_DEFAULT
spanish mexico LANG_SPANISH SUBLANG_SPANISH_MEXICAN
spanish spain (Mod) LANG_SPANISH SUBLANG_SPANISH_MODERN
spanish spain (Trad) LANG_SPANISH SUBLANG_SPANISH_TRADI-
TIONAL
swedish sweden LANG_SWEDISH SUBLANG_DEFAULT
thai thailand
tradChinese taiwan LANG_CHINESE SUBLANG_CHINESE_TRADI-
TIONAL
turkish turkey LANG_TURKISH SUBLANG_DEFAULT
urdu pakistan

Use of the system control panel


NLS information is drawn from the settings of the system control panel. IBM
Smalltalk recognizes a given locale based upon a specific combination of country
and language settings. These parameters are set through the country and language
sections of the International control panel on Windows NT, and via the regional
settings tab of the International control panel on Windows 95 or Windows 98.

An attempt to initialize the current locale for a particular locale will fail unless the
International control panel has the country and language options set appropriately.
For example, on Windows NT, if English U.S. is the desired locale, the
International control panel must have the country set to United States and the
Language set to English (American) before the image is started or the locale is
manually reinitialized. Note that the language must be set to English (American);
that the setting English (International) does not work.

Chapter 13. National Language Support 419


Note:
v Windows 95 and Windows 98 unify the country and language settings
into a single selection in the regional control panel, eliminating
mismatched language and country selections.
v When information is unavailable from the platform, English U.S. default
values are provided. The following table shows the items that are not
available from the operating system, along with their default values.
v The number of decimal digits specified in the number format section of
the International control panel is not used.
v The International control panel setting for monetary leading zero is not
supported and is ignored.
v IBM Smalltalk uses the short date format from the International control
panel; the long date format is ignored.
Table 56. Items not provided by Windows and their default values
Class Item IBM Smalltalk default
value
LCMessages noStr No
yesStr Yes

AIX, HP-UX, and Solaris


The illustration at the beginning of this section shows how each IBM Smalltalk
locale name maps directly to a string describing an operating system locale. The
following tables define the mappings from the IBM Smalltalk locale names to
platform locale names for AIX, HP-UX, and Solaris respectively. Locales that are
not directly supported by the particular operating system are marked in the table
with a horizontal bar.
Table 57. Map from locale names to AIX platform mappings
IBM Smalltalk Locale NameI Platform Mappings
Language Territory Locale Name
arabic arabic AR_AA or ar_AA
croatian yugoCroatian hr_HR
czech czechoslovakia cs_CZ
danish denmark Da_DK or da_DK
dutch belgium Nl_BE or nl_BE
dutch netherlands Nl_NL or nl_NL
english australia
english britain En_GB or en_GB
english canada
english ireland
english new zealand
english us En_US or en_US
estonian estonia
faeroese faeroeIsl
farsi iran
finnish finland Fi_FI or fi_FI

420 IBM Smalltalk: Programmers Reference


Table 57. Map from locale names to AIX platform mappings (continued)
IBM Smalltalk Locale NameI Platform Mappings
Language Territory Locale Name
french belgium Fr_BE or fr_BE
french canada Fr_CA or fr_CA
french france Fr_FR or fr_FR
french switzerland Fr_CH or fr_CH
german austria
german germany De_DE or de_DE
german switzerland De_CH or de_CH
greek greece eI_GR
hebrew israel Iw_IL or iw_IL
hindi india
hungarian hungary hu_HU
icelandic iceland Is_IS or is_IS
italian italy It_IT or it_IT
italian switzerland
japanese japan Ja_JP or ja_JP
korean korea ko_KR
lappish lapland
lettish latvia
lithuanian lithuania
maltese malta
norwegian norway (Bokmal) No_NO or no_NO
norwegian norway (Nynorsk) No_NO or no_NO
polish poland
portuguese brazil
portuguese portugal Pt_PT or pt_PT
russian russia ru_RU
simpChinese china
slovak czechoslovakia sk_SK
spanish mexico
spanish spain (Mod) Es_ES or es_ES
spanish spain (Trad) Es_ES or es_ES
swedish sweden Sv_SE or sv_SE
thai thailand
tradChinese taiwan zh_TW
turkish turkey Tr_TR
urdu pakistan

Chapter 13. National Language Support 421


Table 58. HP-UX and IBM Smalltalk locale names
IBM Smalltalk Locale Name Platform Mapping
Language Territory Locale Name
arabic arabic ar_SA.arabic8 or ar_DX.arabic8 or
ar_SA.iso88596
croatian yugoCroatian hr_HR.iso88592
czech czechoslovakia cs_CZ.iso88592
danish denmark Da_DK.roman8 or da_DK.iso88591
dutch belgium Nl_BE or nl_BE
dutch netherlands Nl_NL.roman8 or nl_NL
english australia
english britain En_GB.roman8 or en_GB.iso88591
english canada
english ireland
english new zealand
english us En_US.roman8 or en_US.iso88591
estonian estonia
faeroese faeroeIsl
farsi iran
finnish finland Fi_FI.roman8 or fi_FI.iso88591
french belgium
french canada Fr_CA.roman8 or fr_CA.iso88591
french france Fr_FR.roman8 or fr_FR.iso88591
french switzerland
german austria
german germany De_DE.roman8 or de_DE.iso88591
german switzerland
greek greece eI_GR.greek8 or el_GR.iso88597
hebrew israel Iw_IL.hebrew8 or iw_IL.iso88598
hindi india
hungarian hungary hu_HU.iso88592
icelandic iceland Is_IS.roman8 or is_IS.iso88591
italian italy It_IT.roman8 or it_IT.iso88591
italian switzerland
japanese japan Ja_JP.SJIS or ja_JP.kana8
korean korea ko_KR.eucKR
lappish lapland
lettish latvia
lithuanian lithuania
maltese malta
norwegian norway (Bokmal) No_NO.roman8 or no_NO.iso88591
norwegian norway (Nynorsk) No_NO.roman8 or no_NO.iso88591

422 IBM Smalltalk: Programmers Reference


Table 58. HP-UX and IBM Smalltalk locale names (continued)
IBM Smalltalk Locale Name Platform Mapping
Language Territory Locale Name
polish poland pl_PL.iso88592
portuguese brazil
portuguese portugal Pt_PT.roman8 or pt_PT.iso88591
russian russia ru_RU.iso88595
simpChinese china zh_CN.hp15CN
slovak czechoslovakia sk_SK.iso88592
spanish mexico
spanish spain (Mod) Es_ES.roman8 or es_ES.iso88591
spanish spain (Trad) Es_ES.roman8 or es_ES.iso88591
swedish sweden Sv_SE.roman8 or sv_SE.iso88591
thai thailand th_TH.tis620
tradChinese taiwan zh_TW.ccdc or zh_TW.big5 or
zh_TW.eucTW
turkish turkey Tr_TR.turkish8 or tr_TR.iso88599
urdu pakistan

Table 59. Solaris and IBM Smalltalk locale names


Smalltalk Locale Name Platform Mapping
Language Territory Locale name
arabic arabic
croatian yugoCroatian
czech czechoslovakia
danish denmark da
dutch belgium nl_BE
dutch netherlands nl
english australia en_AU
english britain en_UK
english canada en_CA
english ireland en_IE
english new zealand en_NZ
english us en_US
estonian estonia
faeroese faeroeIsl
farsi iran
finnish finland su
french belgium fr_BE
french canada fr_CA
french france fr
french switzerland fr_CH

Chapter 13. National Language Support 423


Table 59. Solaris and IBM Smalltalk locale names (continued)
Smalltalk Locale Name Platform Mapping
Language Territory Locale name
german austria
german germany de
german switzerland de_CH
greek greece
hebrew israel
hindi india
hungarian hungary
icelandic iceland
italian italy it
ilian switzerland
japanese japan ja or japanese
korean korea
lappish lapland
lettish latvia
lithuanian lithuania
maltese malta
norwegian norway (Bokmal) no
norwegian norway (Nynorsk) no
polish poland
portuguese brazil pt_BR
portuguese portugal pt
russian russia
simpChinese china
slovak czechoslovakia
spanish mexico es_MX
spanish spain (Mod) es
sish spain (Trad) es
swedish sweden sv
thai thailand
tradChinese taiwan
turkish turkey
urdu pakistan

424 IBM Smalltalk: Programmers Reference


Chapter 14. Inter-process communication
UNIX operating systems provide each process with an environment consisting of
variable names paired with values. This chapter contains the protocol specification
for accessing the processes and environment variables of the host operating
system. These environment variables can be used by the process to determine the
users configuration. Information such as search path, home directory, and user
name can be retrieved from the environment. With access to these environment
variables, a Smalltalk application can query the values and configure itself
appropriately.

The following classes are provided: UNIXEnvironment, UNIXProcess,


UNIXReadPipeStream, and UNIXWritePipeStream.

The UNIXEnvironment class is used by the UNIXProcess class to provide an


environment for new processes.

The UNIXProcess class controls other operating system processes from within
Smalltalk. The UNIXProcess class supports both interactive and passive processes.
Interactive processes support stream-based pipes for stdin, stdout, and stderr.
Stream-based pipes enable the developer to control the process (input) and record
the resulting output. For example, UNIX shell scripts can be invoked from within a
Smalltalk application.

The UNIXReadPipeStream and UNIXWritePipeStream classes support a subset of the


stream protocol. Instances of UNIXReadPipeStream are answered by a UNIXProcess
for stdout and stderr streams, and instances of UNIXWritePipeStream are answered
for input streams.

UNIX environment
This subsection describes a conceptual model of the UNIX environment.

In UNIX, an array of strings called the environment is available to each process.


The format of these strings is aKey=aValue. The environment is often used to store
user configuration information. For example, the PATH environment variable
defines the search path used to locate executables.

The UNIXEnvironment class provides access to the shell environment from within
Smalltalk. A subset of the Dictionary protocol is supported by the UNIXEnvironment
class. Through this protocol the environment can be queried and modified.

There are two intended uses for the UNIXEnvironment class:


1. To enable Smalltalk applications to configure themselves based on available
environment information.
2. To provide the UNIXProcess (described in UNIX process on page 427) with a
configurable environment that can be based on the current environment or
completely defined.

Method specification
Following are UNIXEnvironment methods and usage examples.

Copyright IBM Corp. 1994, 2000 425


UNIXEnvironment class methods
current
Answers the current environment for Smalltalk. This is equivalent to
typing env at a UNIX shell prompt.
getenv:
Gets the environment string association with the specified string. Answers
nil if none. This directly maps to the C function getenv().
new Answers a new instance of the receiver. The new environment is empty.
putenv:
Adds the specified string (in the form, aKey=aValue) to the current
environment. This directly maps to the C function putenv().
startUpEnvironment
Answers the start-up environment for the user process. This provides a
snapshot of the environment in which Smalltalk is running.

UNIXEnvironment methods
at: Answers the object of the receiver that is associated with an object
equivalent to the specified key. Answers nil if the receiver does not contain
an object equivalent to the key.
at:ifAbsent:
Answers the object of the receiver that is associated with an object
equivalent to the specified key. If an equivalent key is not found, answers
the result of evaluating the specified zero argument block. Returns fail if
the key is not found and if the block is not a zero-argument block.
at:put: Associates the specified key with the specified value, in the receiver.
Answers the value currently associated with aKey (or nil if absent). If the
receiver does not contain the specified key, it creates a new entry in the
receiver for that key.
envAt:
Answers the value at the specified key in the receiver in the form
aKey=aValue. If the key is missing, it generates an error.
envAt:ifAbsent:
Answers the value at the specified key in the receiver in the form
aKey=aValue. If an equivalent key is not found, answers the result of
evaluating the specified zero argument block.
envStringAt:ifAbsent:
Answers the key-value pair in the receiver, indicated by the specified key.
If an equivalent key is not found, answers the result of evaluating the
specified zero argument block.
keys Answers the environment keys in the receiver.

Usage example
This section contains examples using the UNIXEnvironment class. Evaluate each of
the expressions by highlighting them and selecting Display from the Edit menu.

The UNIXEnvironment class retains a copy of the environment that was initially
available. This is useful when the current environment has been changed and
access to the original environment is required.
"Retrieve the user's start up environment."
UNIXEnvironment startUpEnvironment

426 IBM Smalltalk: Programmers Reference


In most situations, processes created from within Smalltalk use the current
environment. The current environment can be accessed using the current method.
"Retrieve the current environment."
UNIXEnvironment current

If a custom environment is desired, a new UNIXEnvironment can be created and


defined.
"Create a new environment and set some key-value pairs."
| env |
env := UNIXEnvironment new.
env at: 'TEST' put: '123';
at: 'FRED' put: 'HELLO'.
env

Accessing the environment information is accomplished using the at: method.


"Query the value of a particular environment variable from the user's
start up environment."
UNIXEnvironment startUpEnvironment at: 'HOME'

The keys method answers all of the keys in an instance of UNIXEnvironment. This
method is useful when checking a users configuration.
"Retrieve the names of all environment variables from the user's
start up environment."
UNIXEnvironment startUpEnvironment keys

If you are more familiar with the C language, you might want to use the getenv:
message to access the current environment instead of querying the current
environment and using the at: message. The messages getnev and at: are
functionally equivalent for operations on the current environment.
"Query the Smalltalk AIX process environment with a specific key.
Equivalent to C function."
UNIXEnvironment getenv: 'HOME'

"Query an environment using an instance of UNIXEnvironment."


UNIXEnvironment current at: 'HOME'

Using the putenv: message is similar to the getenv: message except it is used to set a
value instead of query it. The messages putenv and at:put: are functionally
equivalent for operations on the current environment.
"Set a key-value pair in the Smalltalk UNIX process environment.
Equivalent to C function."
UNIXEnvironment putenv: 'TEST=1234'

"Set a key-value pair for an instance of UNIXEnvironment."


UNIXEnvironment current at: 'TEST' put: '1234'.

UNIX process
This subsection describes a conceptual model of the UNIX process.

The C language process model is used by the UNIXProcess class. Each process is
defined as having an input stream (stdin), an output stream (stdout), and an error
stream (stderr). The streams made available by a UNIXProcess are defined when
they are created.

There are three stages in the life of a UNIXProcess:

Chapter 14. Inter-process communication 427


1. Creation of an instance. This includes definition of the environment to be used,
the arguments to be passed to the process, and a Boolean flag for the
input/output/error streams.
2. Reading or writing data on the input/output/error streams (blocking or
non-blocking).
3. Termination, either forced or automatic (blocking or non-blocking).

Through the UNIXProcess class, operating system processes can be controlled from
within Smalltalk.

Three file descriptors, stdin, stdout, and stderr, are available through the C
language. Direct access to these file descriptors is possible for each instance of the
UNIXProcess class. While directly accessing the file descriptors is possible, a
stream-based abstraction is the preferred method of interaction.

An instance of the UNIXWritePipeStream class is used to access stdin while


instances of UNIXReadPipeStream are used to access stdout and stderr. The
UNIXWritePipeStream class and UNIXReadPipeStream class (described in the section
entitled UNIX pipe stream on page 431) support a subset of the stream protocol.
An instance of UNIXProcess responds to the messages stdin, stdout, and stderr by
answering a UNIXPipeStream associated with the operating systems stdin, stdout
or stderr pipe, respectively. If there is no corresponding UNIX pipe (if it was not
requested when the UNIXProcess was created), then nil is answered.

Note: Many UNIX processes that have stdin defined will not terminate unless
stdin has been closed. Sending the message close to the UNIXWritePipeStream
representing stdin closes the pipe and allows the process to end if it is
blocked on input.

Method specification
UNIXProcess methods and usage examples are described in the following section.

UNIXProcess class methods


endAsyncIO
Ends the background Smalltalk process handling asynchronous I/O.
execute:
Creates a process to execute the specified string using the C shell in the
current environment. Starts the process running and answers the process. If
an error occurs starting the process, answers nil.
execute:input:output:error:environment:
Creates a process to execute the specified string using the C shell and the
given environment. Starts the process running and answers the process. If
an error occurs starting the process, answers nil. If any of the I/O
parameters are not nil, initializes the appropriate stdin, stdout, and stderr
pipes.
shell: Invokes a C shell that executes the specified string in the current
environment. Answers the stdout contents and stderr contents
concatenated. If an error occurs starting the process, generates a walkback.
startAsyncIO
Starts a background Smalltalk process to grab I/O for the active
UNIXProcess and moves it between Smalltalk and the operating system.

428 IBM Smalltalk: Programmers Reference


system:
Invokes a C shell that executes the specified string in the current
environment. Answers the integer exitCode of the process. If an error occurs
starting the process, answers nil. If the UNIX primitive for completion
checking fails, generates a walkback.

UNIXProcess methods
closePipe:
Closes the specified pipe. Generates a walkback if the pipe is not valid.
complete
Obsolete protocol retained for compatibility. It should be replaced with
isComplete
exitCode
Answers an integer representing the receivers exitCode. If the process has
not completed, answers nil. This is operating-system dependent.
fillPipe:
Flushes all data in the pipe from the operating system process associated
with the receiver to the Smalltalk UNIXReadPipeStream. Generates a
walkback if the specifed pipe stream is not an instance of
UNIXReadPipeStream or is not a valid UNIXPipeStream.
id Obsolete protocol retained for compatibility. Should be replaced with pid.
isComplete
Answers true if the receiver process has completed, answers false if not. If
the UNIX primitive for completion checking fails, generates a walkback.
kill Kills the receiver process. Try using the SIGTERM signal first. If the
process does not die within 5 seconds, sends the SIGKILL signal. This
method blocks until the process dies. Answers the receiver.
nextFrom:
Answers the next available character from the receivers stdout. If the read
would block, answers nil. On any other error answers a character with a
value of zero.
nextPut:on:
Writes a character to the receivers stdin pipe, specified by on:. Generates a
walkback if the pipe is not valid. Answers the character.
pid Answers the integer representing the process id of the receivers operating
system process. If there is no corresponding operating system process,
answers nil.
run Obsolete protocol retained for compatibility. The
execute:input:output:error:environment: message encompasses this behavior.
stderr Answers a UNIXReadPipeStream representing the receivers stderr pipe, or
nil if the pipe is undefined.
stdin Answers a UNIXWritePipeStream representing the receivers stdin pipe, or
nil if the pipe is undefined.
stdout Answers a UNIXReadPipeStream representing the receivers stdout pipe, or
nil if the pipe is undefined.
waitForCompletion
Ensures the receiver has finished the last operation. Answers the exitCode
of the process. This is a blocking operation. If the stdin pipe has not been
closed, the operation may block indefinitely.

Chapter 14. Inter-process communication 429


Usage example
This section contains examples using the UNIXProcess class. Evaluate each of the
expressions by highlighting them and selecting Display from the Edit menu.

One common use for a UNIXProcess is to invoke shell commands from within
Smalltalk and capture the output.
"Display on the Transcript the result of a shell command."
Transcript show: (UNIXProcess shell: 'ls -al')

To invoke a command and not capture the output, the system message is used.
"Perform a shell command and direct output to the start up terminal"
UNIXProcess system: 'env'

In some situations the shell: and system: messages do not provide sufficient control
over the process. The following example shows the three stages of a UNIXProcess:
creation, the reading and writing of data, and termination.
"Connect user defined streams to the UNIX process stdin, stdout
and stderr."
| process output input result |

process := UNIXProcess "Create the process."


execute: 'cat'
input: true
output: true
error: nil
environment: UNIXEnvironment current.
input := process stdin.
output := process stdout.
input
nextPutAll: 'This is input sent into standard in';
close.
process waitForCompletion. "Wait for process to terminate."
output contents

Using the stream interface, it is possible to interact with a process. Future input to
the process can be determined by the currently available output or state of the
process.
"It is not necessary to provide all input immediately."
| process output input result |
process := UNIXProcess "Create the process."
execute: 'cat'
input: true
output: true
error: nil
environment: UNIXEnvironment current.
input := process stdin.
output := process stdout.
input
nextPutAll: 'This'; "Perform read/writes of data."
flush.
process isComplete ifTrue: [self error: 'Process should not be complete'].
input
nextPutAll: ' is input sent into standard in';
close.
process waitForCompletion. "Wait for process to terminate."
output contents

The following code is an example method for an application. The method makes
use of the UNIXProcess class to mail a message to a collection of recipients with a
given subject. A method similar to this could be used to interface to the UNIX mail
facilities from within Smalltalk.

430 IBM Smalltalk: Programmers Reference


Note: Highlighting the following example code and selecting Display will not
work. It is a method and requires parameters.
"Send mail and ignore the reply."
mail2: recips subject: subject msg: string
| process args env |
args:= '/usr/ucb/mail'.
recips do: [:person | args := args, ' ', person].
env := UNIXEnvironment current.

process := UNIXProcess
execute: args
input: true
output: true
error: nil
environment: env.
process stdin
nextPutAll: 's ',subject; cr;
nextPutAll: string; cr;
close.
process waitForCompletion.
|process exitCode

UNIX pipe stream


This subsection describes a conceptual model of the UNIX pipe stream.

In the C language, three file descriptors are made available to processes: stdin,
stdout, and stderr. The stdin file descriptor is used to provide input to the process,
while stdout and stderr are for output and error output respectively. The Smalltalk
interface to these pipe handles is either an instance of UNIXWritePipeStream or
UNIXReadPipeStream.

Both the UNIXWritePipeStream and UNIXReadPipeStream classes respond to a subset


of the stream protocol. The pipe stream interface permits modification of the
position in the stream. This means that a UNIXReadPipeStream can be reset and
re-read; similarly, a UNIXWritePipeStream can be reset and the same input flushed a
second time.

Instances of UNIXWritePipeStream and UNIXReadPipeStreams should never be


created directly. They should be requested when the process is created through the
execute:input:output:error:environment: message supported by UNIXProcess.

UNIX read pipe stream method specification


UNIXReadPipeStream methods are described in the following section.

UNIXReadPipeStream class methods


for: Answers a new, initialized instance of a UNIXReadPipeStream for the
specified process.

UNIXReadPipeStream methods
atEnd Answers a Boolean that is true if the receiver cannot access any more
objects, otherwise answers false.
blocking:
Sets a Boolean that indicates whether or not the receiver will block on I/O
operations. Answers the receiver.
close Closes the receivers associated UNIX pipe. If any unread information is

Chapter 14. Inter-process communication 431


available from the operating system, this is transferred to the receiver.
Closes the operating system pipe. The receiver is no longer valid for fill
operations. Answers the receiver.
contents
Answers the current contents of the receiver as a String.
copyFrom:to:
Answers the subcollection of the collection over which the receiver is
streaming, from the first specified index to the last specified index. If the
receiver is blocking, enough data is read to satisfy the request, or until
atEnd is true. If the receiver is non-blocking, then it uses only currently
available data.
do: Evaluates the specified block once for each element in the receiver, from
the element after the current position to the end. If the receiver is blocking,
data is read until atEnd is true. If the receiver is non-blocking then it uses
only currently available data.
fill Guarantees that any pending data from the receivers UNIX process has
been flushed to the pipe stream. Answers the receiver.
isBlocking
Answers true if the receiver has been set to block on I/O, otherwise
answers false.
isBytes
Answers true if the receiver handles I/O data as bytes, otherwise answers
false.
isBytes:
If flag is true, sets the receivers I/O data type to bytes. If flag is false, sets
the I/O data type to characters. Answers the receiver.
isCharacters
Answers true if the receiver answers I/O data as characters.
isCharacters:
If flag is true, sets the receivers I/O data type to characters. If flag is false,
sets the I/O data type to bytes. Answers the receiver.
isEmpty
Answers true if the receiver pipe stream currently contains no elements,
otherwise answers false.
lineDelimiter
Returns the line delimiter of the receiver.
lineDelimiter:
Sets the line delimiter of the receiver.
next Answers the next character accessible by the receiver and advances the
receivers position. Answers nil if the read would block. Reports an error if
no data is available to be read.
next: Answers the next specified integer number of items from the receiver,
returned in a collection of the same type as the collection being streamed
over. If the receivers state is such that there are fewer than the specified
number of integer elements between its current position and the end of the
stream, the operation fails, and the receiver is left in a state such that it
answers true to the atEnd message.

432 IBM Smalltalk: Programmers Reference


nextLine
Answers the elements between the current position and the next
lineDelimiter.
nextMatchFor:
Answers true if the next element which is accessible by the receiver is
equal to the target, otherwise answers false.
peek Answers the next object in the receiver stream without advancing the
receivers position. If the receiver is positioned at the end, answers nil.
peekFor:
Answers true if the next object to be accessed in the receiver stream equals
the specified byte or character, otherwise answers false. Only advances the
receivers position if the answers is true.
position
Answers the current position of the receiver.
position:
Sets the current position of the receiver to the specified integer. If the
position is out of bounds, sets the position to the closest valid position.
reset Positions the receiver to the beginning of the collection being streamed
over. Answers the receiver.
setToEnd
Sets the receivers position reference to be the size of the underlying
contents. Answers the receiver.
size Answers the number of elements in the receiver.
skip: Increments the position of the receiver by the specified integer. Fails if the
argument is not a kind of integer. Answers the receiver.
skipTo:
Advances the receivers position beyond the next occurrence of the
specified object, or if none, to the end of stream. Answers true if the object
occurred, otherwise answers false.

Note: skipTo: is a blocking operation.


skipToAll:
Advances the receivers position beyond the next occurrence of the
elements of the sequential collection, or if none, to the end of stream.
Answers true if the elements of the sequential collection occurred,
otherwise answers false. Non-blocking streams operate on the currently
available data.
upTo: Answers the collection of objects from the receiver starting with the next
accessible object and up to but not including the specified object. Sets the
position beyond the object. If the object is not present, answers the
remaining elements of the stream. Non-blocking streams operate on the
currently available data.
upToAll:
Answers the collection of objects from the receiver starting with the next
accessible object and up to but not including the specified sequential
collection. Sets the position beyond the sequential collection. If the
sequential collection is not present, answers the remaining elements of the
stream. Non-blocking streams operate on the currently available data.

Chapter 14. Inter-process communication 433


upToEnd
Answers the subcollection of the collection over which the receiver is
streaming, from the current position to the end of the accessible elements.
Non-blocking streams operate on the currently available data.

UNIX write pipe stream method specification


UNIXWritePipeStream methods are described in the following section.

UNIXWritePipeStream class methods


for: Answers a new, initialized instance of a UNIXWriteReadPipeStream for the
specified process.

UNIXWritePipeStream methods
atEnd Answers a Boolean that is true if the receiver cannot access any more
objects, otherwise answers false.
blocking:
Sets a Boolean that indicates whether or not the receiver will block on I/O
operations. Answers the receiver.
close Closes the receivers associated UNIX pipe. If any unwritten information
remains in the receiver, it is flushed to the operating system before the
pipe is closed. The operating system pipe is closed, and the receiver is no
longer valid for flush or write operations. Answers the receiver.
contents
Answers the current contents of the receiver as a String.
cr Writes a logical carriage return to the receiver.

Note: Currently, all write operations are blocking.


flush Guarantees that all available data in the receiver is sent to the operating
system pipe. This is a blocking operation. Answers the receiver.
isBlocking
Answers true if the receiver has been set to block on I/O, otherwise
answers false.
isBytes
Answers true if the receiver handles I/O data as bytes, otherwise answers
false.
isBytes:
If flag is true, sets the receivers I/O data type to bytes. If flag is false, sets
the I/O data type to characters. Answers the receiver.
isCharacters
Answers true if the receiver handles I/O data as characters.
isCharacters:
If flag is true, sets the receivers I/O data type to characters. If flag is false,
sets the I/O data type to bytes. Answers the receiver.
isEmpty
Answers true if the receiver pipe stream currently contains no elements,
otherwise answers false.
lineDelimiter
Returns the line delimiter of the receiver.

434 IBM Smalltalk: Programmers Reference


lineDelimiter:
Sets the line delimiter of the receiver to be the specified string.
next:put:
Answers the specified object. Puts the object to the receivers pipe the
number of times specified by next:. Fails if next: does not specify an integer.
nextPut:
Writes a byte or a character to the receivers pipe. Answers the receiver.
nextPutAll:
Writes the specified sequential collection to the receivers pipe. Answers
the sequential collection.
position
Answers the current position of the receiver.
position:
Sets the current position of the receiver to the specified integer. If the
position is out of bounds, sets the position to the closest valid position.
reset Positions the receiver to the beginning of the collection being streamed
over. Answers the receiver.
setToEnd
Sets the receivers position reference to be the size of the underlying
contents. Answers the receiver.
size Answers the number of elements in the receiver.
skip: Increments the position of the receiver by the specified integer.
space Writes a space character to the receivers pipe.
tab Writes a tab character to the receivers pipe.

Chapter 14. Inter-process communication 435


436 IBM Smalltalk: Programmers Reference
Chapter 15. Object Linking and Embedding (OLE)
This chapter describes how to use the OLE application framework to build
OLE-enabled client applications.

First, what is OLE? This is the definition from Inside OLE (Second Edition) by
Kraig Brockschmidt of Microsoft:

OLE is a unified environment of object-based services with the capability of


both customizing those services and arbitrarily extending the architecture
through custom services, with the overall purpose of enabling rich integration
between components.

OLE is Microsofts entry into the world of object-oriented computing. At the lowest
level, object-based services are provided by C++ classes. These classes are called
interfaces. Interfaces can be defined in any language but are most easily defined in
C++. When one component needs to talk to another, it queries for a well-known
interface. If the query succeeds, an instance of the interface is returned.
Functionality is accessed using the methods of the interface. Access to the private
state of the instance is not allowed.

To ensure binary compatibility and language independence in OLE, interfaces are


well known and strictly defined. The fact that interfaces resemble C++ classes is
incidental. Inheritance is not supported and methods are used in the same way
that jump tables to functions are used in standard C.

Microsoft has defined a client/server environment for Windows where a client is


defined as the user of a service and a server is defined as the provider. It is useful
to partition OLE functionality in terms of client/server to focus on how OLE is
used. For example, OLE provides custom control services (more commonly known
as OCXs). An OCX client is any application that uses the OCX. An OCX server
implements the OCX.

OLE programming is complex, error-prone, and time-consuming. To program OLE,


it is necessary to implement and query many interfaces during the lifetime of the
program. Because interfaces come from other applications, their behavior may or
may not conform to the interface specification. In many cases, the specification is
vague, making it necessary to determine behavior by experimentation.

The problem is further compounded by the fact that when writing an interface,
much of the implementation can be easily and trivially defaulted, yet must be
present for the interface to work. This leads to increased code bulk and complexity.
The fact that there are multiple interfaces that are often tightly coupled leads to
design confusion, which is often resolved by trial and error by the less-experienced
OLE programmer.

To overcome the complexity of OLE, development environments normally provide


a higher-level OLE application framework:
Visual C++
Microsoft provides the Microsoft Foundation Classes (MFC), a C++ class
library, that comes with Visual C++. The MFC hierarchy encapsulates OLE

Copyright IBM Corp. 1994, 2000 437


(and Windows) in a C++ class library. Using MFC, you can access most of
OLE without using low-level OLE interfaces.
Visual Basic
Extensions to the Visual Basic widgets and the usual syntax of Visual Basic
expose OLE.
Smalltalk
The OLE application framework provides high-level support for writing
OLE clients in Smalltalk.
The framework provides high-level abstractions for the OLE container
concepts, thereby keeping the low-level OLE interfaces transparent to you.
Using this framework, you can write OLE container applications that
support embedding, linking, in-place activation, drag/drop, and structured
storage. The framework supports writing OLE automation controllers,
whereby third-party automation servers (such as Microsoft Word or
Microsoft Excel) can be programmed from Smalltalk. In addition, you can
easily and quickly incorporate arbitrary, off-the-shelf OCXs into the
Smalltalk image and materialize them as Common Widgets.

OLE concepts and definitions


Before building an OLE-enabled application, it is helpful to become familiar with
the following concepts and definitions.
OLE client
An OLE client is an application that accesses and uses the functionality
and content of a OLE server application. The communication between an
OLE client and an OLE server is through OLE object interfaces. VisualAge
clients can connect to OLE servers regardless of the servers
implementation language.
OLE server
An OLE server is the provider of a component and its constituent objects.
You can implement OLE servers as DLLs or as complete applications
operating in an execuatable.
Compound document
A compound document is an application that results in the seamless
integration of data of different formats, such as spreadsheets, sound clips,
text, and bitmaps. A compound document is an OLE client that integrates
data and functionality from a variety of OLE servers. End users perceive
they are using a single application that provides all the function of the
respective servers.
OLE container
An OLE container is an OLE client that consumes compound-document
objects, OCXs, or both. OLE containers typically provide:
v Storage for the contained OLE objects
v A place in their user interface for the OLE objects to display their
contents
v Access to the editing facilities of the server for the OLE objects.
Embedded OLE objects
An OLE object is embedded in its container when its data is physically
stored with the container. As a result, the OLE object becomes a physical
part of its container.

438 IBM Smalltalk: Programmers Reference


Linked OLE objects
An OLE object is linked to its container when its data continues to
physically reside where it was initially created. Only a reference (that is, a
link) to the object and the appropriate presentation data are maintained by
the container.
Activation
Activation is the process of making an OLE server active on the contents
of one of its embedded or linked OLE objects that are in a container. A
container does not directly reference or modify the content of its contained
OLE objects, so the respective servers are activated to provide facilities so
you can edit and manipulate the contained content.
Verb OLE servers are activated by invoking an action on them; the action is
called a verb. The most common verb is the edit verb, which activates the
server in a state for editing the contents of the embedded or linked object.
OLE servers always designate one verb as the primary verb, which is most
often the edit verb. However, a sound or video object may have play as its
primary verb and edit as a secondary verb. Many OLE document servers
support more than one verb.
A container typically displays an OLE objects verbs as commands on one
of its own menus. When you select a verb, the container passes it to the
server for execution. When the server receives a verb, it may open as usual
in its own window to allow you to edit the OLE object, or it may attempt
to interact with the user directly inside the containers window. The latter
is called in-place activation.
In-place activation
OLE objects that are embedded in a container application can be edited
(that is, activated) in place, which means that all maintenance of the object
takes place within the container. When an OLE object is activated in place,
the menu bar for the container becomes a mixture of the current container
menus and the menus provided by the server. In addition, real estate in
the containers windows is negotiated for any of the tool bars and floating
palettes that the server provides for editing the object.
Since the content of a linked OLE object is not stored with the container,
linked objects cannot be activated in place; they are activated in the
windows of their server.
OLE automation client
An OLE automation client is an application that accesses functionality
provided by third-party applications. OLE automation is a standard that
allows communication between applications. For example, a program can
query and set the current text selection in a Microsoft Word document
through OLE automation.
OLE automation server
An OLE automation server makes functionality available by exposing OLE
objects. These objects have properties and methods. An OLE property is a
value that an application can get and set. An OLE method is a
multi-parameter function that is called by an automation client and
implemented by an automation server.
OLE custom control (OCX)
An OCX is a custom Windows control that is built with OLE technologies.
Unlike a Windows control, an OCX may or may not have a visual
representation. OCXs build on almost all the base OLE technologies. For
example, an OCX uses:

Chapter 15. Object Linking and Embedding (OLE) 439


v In-place activation for its content display in a client applications user
interface
v The server side of OLE automation for control of its properties and
behavior
v The client side of OLE automation for event notification from the server
to the client.
Properties
A property is a data attribute (or a variable) of an OLE automation object
or an OCX that is retrieved or modified by a container. Properties of an
OLE automation or OCX object are identified by both a unique name and a
unique numeric value.
Ambient properties
Ambient properties are attributes of an OCX container that are supplied
on request to any of its embedded OCXs. These properties define the
environment (or ambiance) for all OCXs used in an application, for
example, default colors and fonts. OCXs typically initialize their state
through ambient-property requests to the container. However, containers
may explicitly override an OCX property that was obtained from its
ambient property.
Event An event is a notification that an OCX sends to its container when the user
triggers an event in that OCX, for example, a mouse-click on the OCX. An
OCX uses events to transform a users action into a precise trigger on its
container, which in turn picks up the event and executes some code.
DISPID
Each method or property of an OLE automation or OCX object has a
unique name and a unique DISPID (a 32-bit number). A DISPID is the
preferred way to access a method or property because it reduces expensive
string lookup operations and is independent of the national language used
to define the names.
Licensing
Licensing is a mechanism that OCX vendors use to restrict the use of their
OCX in container applications. Licensed OCXs provide a key that a
container must use to activate the OCX at run time.

OLE classes
The OLE application framework consists of the following classes:

OleMainWindow
OleMainWindow is a subclass of CwMainWindow. It extends the functionality of a
main window to support menu-bar negotiation when an object is activated in
place. You must use an OleMainWindow instead of CwMainWindow in any
application that embeds or links OLE objects or uses OCXs.

When an embedded OLE object is activated in place, the menu bar for the
container becomes a mixture of the containers current menus and the menus
provided by the server. Menu-bar items are divided into these groups:
v File
v Edit
v Container
v Object
v Window
v Help

440 IBM Smalltalk: Programmers Reference


When an object is activated in place, the container provides these groups:
v File
v Container
v Window

The server provides these groups:


v Edit
v Object
v Help

A container provides the OleMainWindow with the pull-down menus for each of
the groups it is responsible for. For example, a container might specify its in-place
menu-bar groups as follows:
anOleMainWindow
fileMenuGroup: (Array with: fileMenuCascade);
containerMenuGroup: (Array with: optionsMenuCascade with: featuresMenuCascade);
windowMenuGroup: (Array with: windowMenuCascade)

Hence, when an embedded OLE object is activated in place, a merged menu bar is
built and managed by the OleMainWindowfirst using the menus specified by the
container, and then querying the server for its menu groups. In addition, real estate
in the containers OleMainWindow is negotiated for any tool bars and floating
palettes that the server provides for editing the object. When the embedded object
is deactivated, the original menu bar of the OleMainWindow is restored along with
any window areas used by the server for tool bars and so on.

OlePrimitive
OlePrimitive is a subclass of CwPrimitive. It defines common behavior to widgets
that provide sites for OLE embedded, linked, or OCX objects. Applications that use
OlePrimitive widgets must use OleMainWindow in their runtime widget hierarchy
instead of CwMainWindow.

OleClient
The OleClient class is a subclass of OlePrimitive. It allows OLE objects from other
applications to be embedded or linked into an IBM Smalltalk or VisualAge
application. An OleClient contains the OLE object and allows that object to behave
like other Common Widgets.

With OleClient, you can do the following:


v Create a placeholder in the containers user interface to contain an embedded or
linked OLE objectthe OLE object that appears within the OleClient is created at
run time
v Insert embedded or linked OLE objects directly from their server into a container
v Create embedded or linked OLE objects from a server-generated file
v Create embedded or linked OLE objects from the clipboard or from dragging or
dropping between other OLE-enabled third-party applications
v Display embedded or linked OLE objects as icons
v Activate an embedded OLE object in place if in-place activation is supported by
its server
v Within the containers user interface, allow for resizing and movement of an
embedded or linked OLE object.

Chapter 15. Object Linking and Embedding (OLE) 441


OleControl
The OleControl is a subclass of OlePrimitive. It allows you to insert OCXs and use
them in an IBM Smalltalk or VisualAge application. An OleControl widget contains
an OCX and allows that object to behave like any other CwPrimitive.

With OleControl, you can do the following:


v Create a placeholder in the containers user interface to contain an OCX
v Set properties and the control function of the contained OCX
v Use the Common Widgets event-handler mechanism to trap and process events
generated by the contained OCX
v Provide hooks that allow the OCX access to specific properties of the
applications user interface components, for example, access to an applications
ambient properties
v Use licensed OCXs within an application

OleAutomationObject
An OleAutomationObject provides a programming interface to OLE automation
objects exposed by an OLE server. OLE automation objects contain data
(properties) and the functions (methods) used to perform an action with the object.
For example, Microsoft Excel handles all the operations and data associated with a
range of cells in a spreadsheet. With OleAutomationObject, you can do the
following:
v Create an instance of OleAutomationObject that references an OLE automation
server object through a name, through a server file, or from an existing
embedded or linked OleClient object
v Set or retrieve properties of an OLE automation server object
v Invoke methods on an OLE automation server object.

OleFile
An OleFile enables you to transparently persist embedded or linked OLE objects
into files. An OleFile works with an OleClient or OleControl widget to perform
persistence operations on their contained OLE objects. Items stored into an OleFile
are identified by a unique name that you provide when the object is saved.

Using instances of OleFile, you also can associate arbitrary data attributes with a
stored OLE object. These attributes are referenced for storage and retrieval by
using the same name the object was stored under. You can use this feature, for
example, to store various attributes of the OleClient widget containing the OLE
object, such as, its position, width, and height.

There is an additional facility in OleFile to store and retrieve a set of data attributes
that may be global to all the OLE objects stored within the file.

Building an OLE document container


An OLE document container is an application that supports embedding or linking
objects from other OLE-enabled applications. The document container provides a
place in its user interface for the OLE object to display its contents (or its icon) and
optionally allows that object to be edited by its server. The container may also
allow embedded or linked OLE objects to be:
v Physically moved or resized within its own windows
v Copied to the clipboard for pasting into another application

442 IBM Smalltalk: Programmers Reference


v Dragged to another application

In addition, a container may save the data associated with an embedded OLE
object directly into one of its own files.

The steps to create an OLE-enabled application that will contain OLE objects are:
1. As part of the initialization of the user-interface widget hierarchy, create an
OleMainWindow and enable its menu groups to support in-place activation of
any descendent OleClient widgets.
2. Implement methods to create OleClient widgets and create the OLE objects that
will appear within their borders.
3. Implement methods to enable copying, cutting, and pasting OLE objects to and
from the clipboard.
4. Create and manage a menu containing the OLE objects verbs.
5. Invoke the Links dialog to allow a user to manage the links of any linked OLE
objects.
6. Save and restore embedded or linked OLE objects to and from files.

Creating an OLE main window


OLE-enabled applications must use the OleMainWindow class rather than
CwMainWindow. This is because OLE main windows can negotiate with OLE
servers for menu-bar space and window real estate for in-place activation of any
OLE object contained in its widget tree. An OleMainWindow is a subclass of
CwMainWindow and, as such, it is created in the same way. The helper method
createOleMainWindow: in CwWidget provides typical widget-creation protocol.

For example, a method of an OLE-enabled application that creates an


OleMainWindow under a shell window might look like:
createMainWindow: shell
"Private - Create and answer the main window for the receiver."
|(shell
createOleMainWindow: 'main'
argBlock: [:w | w hostName: 'Container']) manageChild

Here, the hostName resource is set even though it is often only used by
non-in-place-activated OLE servers. In the case of non-in-place activation, the
hostName may be displayed on the label of an OLE servers Close or Exit menu
items. For example, given the hostName above, the label of an activated OLE
servers normal termination menu item might read Exit to Container instead of
simply Exit, as it would when the server application is explicitly launched. The
menu bar of an OleMainWindow is partitioned into groups to facilitate the
negotiation for menu-bar space during in-place activation. The groups are:
v File
v Edit
v Object
v Container
v Window
v Help

Each group contains one or more pull-down menus (CwCascadeButton widgets).


During in-place activation, the container is asked to populate the File, Container,
and Window groups with pull-down menus appropriate to those categories of

Chapter 15. Object Linking and Embedding (OLE) 443


function. After the container populates its groups, the server is asked to fill in the
Edit, Object, and Help groups with pull-down menus appropriate for its editing
operations.

A containers menu-bar negotiations with an OLE server are handled automatically


by OleMainWindow. As a result, all you need do to facilitate these negotiations is
provide the pull-down menus for any of the three groups it is interested in.
OleMainWindow has the following instance methods for each designated menu
group.
File fileMenuGroup:
Container
containerMenuGroup:
Window
windowMenuGroup:

Each method takes an Array containing CwRowColumn widgets representing the


menus for the group.

For example, the createMenuBar: method of an OLE container might have five
pull-down menus defined for its normal operation and specify three of them for
use when any contained OLE object is activated in place:
createMenuBar: mainWindow
"Private - Create and answer the menu bar for the receiver."
| menu children |
menu := mainWindow
createSimpleMenuBar: 'bar'
argBlock: [:w |
w
buttons: #('File' 'Edit' 'Container' 'Window' 'Help');
buttonMnemonics: #($F $E $C $W $H)].

children := menu children.


mainWindow
fileMenuGroup: (Array with: (children at: 1));
containerMenuGroup: (Array with: (children at: 3));
windowMenuGroup: (Array with: (children at: 4)).
menu manageChild.
|menu

During in-place activation, all other negotiations for window real estate (for
example, tool-bar and status-bar space) between an OLE container and an OLE
server are transparently handled by the OleMainWindow class. The menu group
cannot be changed while an OLE client is active in place.

Creating OLE clients


An OleClient widget provides an OLE container the place to put embedded or
linked OLE objects. An OleClient is a primitive widget, thus conforms to all
standard management of widget geometry. An OleClient widget contains a
connection to an embedded or linked OLE object and transparently manages all
low-level OLE interaction with that object. The contained OLE object renders its
content on the OleClient widget, which may be configured to allow the user to
resize its area or move it within its enclosing window.

OleClient widgets are created and managed like other primitive widgets. However,
there are several techniques for creating and connecting an embedded or linked
OLE object to an OleClient widget.

444 IBM Smalltalk: Programmers Reference


Some techniques create and connect an embedded or linked OLE object at the
same time as the OleClient widget is created. These include creating an OLE object
by its registered name or from one of its server files.

Other techniques create and connect the OLE object after the widget has been
created but before it is managed in the widget tree. These include creating an OLE
object from the following:
v An invocation of the Insert Object dialog
v The clipboard through the Paste Special dialog
v The clipboard without going through the Paste Special dialog
v A drag and drop operation that uses the framework described in Chapter 9.
Drag and Drop on page 263.

Creating by name
To create an OLE object and connect it with an OleClient widget, the OLE objects
server must be registered in the system registry, which is often called the
registration database. One of the attributes of the registration is the
programmatic identifier, or ProgId, which identifies the registered OLE object. The
ProgId of a registered OLE object is the name used for the clientName resource of
an OleClient.

Since OLE clients are created as child widgets of non-primitive widgets (that is,
any CwComposite), there is an additional widget-creation convenience method
implemented in CwWidget called createOleClient:argBlock:. Hence, knowing an OLE
objects ProgId, an OLE client is simply created and connected to the OLE object.

For example, an embedded Microsoft Word document is created using:


| client |
client := composite
createOleClient: 'client'
argBlock: [:w |
w
x: 50;
y: 50;
width: 200;
height: 200;
clientType: XmEMBEDDED;
clientName: 'Word.Document.6'].
client manageChild.

Here, the clientType resource is set to XmEMBEDDED and specifically requests that
the OLE object named Word.Document.6 be embedded into the new OleClient.
This code fragment creates an OleClient widget that contains an empty embedded
Microsoft Word document. Double-clicking anywhere on the OleClient widget
causes Microsoft Word to be activated in place and ready for editing the embedded
document.

Creating from a file


Creating an OleClient from an OLE object servers data file is similar to creating by
name. To create from a file, the file must already be created by the OLE server.

An embedded OLE object can be created from an existing Microsoft Word


document file using:
| client |
client := composite
createOleClient: 'client'
argBlock: [:w |
w
x: 50;

Chapter 15. Object Linking and Embedding (OLE) 445


y: 50;
width: 200;
height: 200;
clientType: XmEMBEDDED;
sourcePath: 'c:\docs\sample.doc'].
client manageChild.

The OleClient is created to be linked to the same document file by changing the
clientType resource to XmLINKED. For example, an OleClient is created to be linked
to a Microsoft Word document file using:
| client |
client := composite
createOleClient: 'client'
argBlock: [:w |
w
x: 50;
y: 50;
width: 200;
height: 200;
clientType: XmLINKED;
sourcePath: 'c:\docs\sample.doc'].
client manageChild.

The embedded and linked cases look the same to the user. However, in the linked
case, when the user double-clicks on the OleClient, Microsoft Word opens in its
own windows for editing the linked document; it does not activate in place in the
container. Furthermore, since the OleClient is linked to the file, other users can
separately edit the file and modify its contents. However, an OleClient can be
explicitly updated to reflect third-party changes in the linked OLE objects content
by sending the OleClient the updateFromLink message.

Creating from the Insert Object dialog


Many containers let their users insert registered OLE objects into the application.
To support this, the OleClient widget provides access to the standard OLE Insert
Object dialog. This dialog can be invoked from an existing OleClient through its
promptInsertObject: method.

Applications typically offer access to this dialog through one of their menu items.
In fact, the OLE user-interface guidelines suggest offering an Insert Object item on
either an Edit or Insert pull-down menu. Before the Insert Object dialog can be
invoked, an OleClient widget must already exist and must not be connected to an
OLE object. For example, an Insert Object menu item callback might look like:
menuEditInsertObject
"Private - Process the Edit menu Insert item."
| client result |

446 IBM Smalltalk: Programmers Reference


client := composite
createOleClient: 'client'
argBlock: [:w |
w
x: 20;
y: 40;
width: 200;
height: 200].
client
mappedWhenManaged: false;
manageChild.
result := client promptInsertObject: XmANY.
result == nil ifTrue: [|client destroyWidget]. "Dialog canceled"
result
ifTrue: [
client mappedWhenManaged: true.
|self].
ifFalse: [
CwMessagePrompter warn:
('The server application, source file, or item cannot be found.\',
'Make sure the application is properly installed, and that it \',
'has not been deleted, moved or renamed.') addLineDelimiters].

In this example, the OleClient is first created with an initial position and size. The
OleClient widget is then managed. It is not, however, made visible to you using
mappedWhenManged: false until the embedded or linked OLE object is created and
connected to the widget. The parameter to promptInsertObject: specifies what kind
of OLE object can be inserted in the widget. If XmANY is specified, for example,
you can select the Create from File option in the dialog and then optionally select
Link.

For each resource value designated below, you can take the designated action with
the inserted OLE object:
XmANY
Can be embedded or linked
XmLINKED
Must be linked
XmEMBEDDED
Must be embedded

If promptInsertObject: answers true, an OLE object was created and connected to the
OleClient widget, which can then be mapped with the message client
mappedWhenManaged: true.

If promptInsertObject: answers nil, you canceled the dialog. However, if it answers


false, the creation of the OLE object failed.

If you insert an object from a file, whether it is linked or not, the widget displays
the contents of that OLE object. However, if the OLE object is inserted as
embedded, there is nothing to display, and the OLE object is immediately activated
to let you enter some initial content.

Note: An OleClient can be queried to determine whether it is embedded or linked


by sending isEmbedded or isLinked.

Creating from the Paste Special dialog


When an OLE object is copied to the clipboard, its server renders the object in
many different formats, many of which are required by OLE for interoperability
with other OLE-enabled applications.

Chapter 15. Object Linking and Embedding (OLE) 447


The OLE system provides a standard dialog called Paste Special, which allows you
to select which object format to paste into their application. An OleClient widget
provides access to the Paste Special dialog through its promptPasteSpecial: instance
method.

As with the Insert Object dialog, applications typically offer access to the Paste
Special dialog through one of their menu items. Again, the OLE user interface
guidelines suggest offering a Paste Special item on the Edit pull-down menu.
Before the Paste Special dialog can be invoked, an OleClient widget must already
exist and must not be connected to an OLE object. For example, a Paste Special
menu item callback might look like:
menuEditPasteSpecial
"Private - Process the Edit menu Paste Special item."
| client |
client := composite
createOleClient: 'client'
argBlock: [:w |
w
x: 20;
y: 40;
width: 200;
height: 200].
client
mappedWhenManaged: false;
manageChild.
(client promptPasteSpecial: XmANY) == true
ifFalse: [|client destroyWidget].
client mappedWhenManaged: true

In this example as in the one before it, the OleClient is first created with an initial
position and size. The OleClient widget is then managed but not made visible to
you until the OLE object is created and connected to the widget. The parameter to
promptPasteSpecial: takes the same values as promptInsertObject:. If XmANY is
specified, for example, you can select the Paste Link option in the dialog to create
a linked OLE object.

If promptPasteSpecial: answers true, an OLE object was created and connected to the
OleClient widget. The widget can then be mapped and the OLE object made
visible.

If promptPasteSpecial: answers nil, you canceled the dialog. However, if it answers


false, the creation of the OLE object failed.

OLE servers render a variety of formats on the clipboard when one of their objects
is copied. With the Paste Special dialog, you can select for embedding one of the

448 IBM Smalltalk: Programmers Reference


non-object renderings, such as a metafile or bitmap. In this case, the image of the
rendering appears in the OleClient widget when it is mapped, but it you cannot
activated it. This is known as static embedding. The instance method
isStaticEmbedding is available to query an OleClient to see whether it has a static
embedding. For example, if a container application disallows static embedding, it
could test using:
((client promptPasteSpecial: XmANY) x= true or: [client isStaticEmbedding])
ifTrue: [|client destroyWidget].

Enabling clipboard operations


OLE containers often allow any embedded or linked OLE object to be copied to the
system clipboard for pasting into another application. To support this activity, the
OleClient class provides a special clipboard format rendering that manages the
underlying OLE formats required to make this work.

This format is OLE object and its rendering is obtained from an OleClient through
its instance method oleObjectDescriptor. This special OLE object clipboard rendering
is copied to the clipboard using the standard Common Graphics clipboard
operations on a CgDisplay object:
v clipboardStartCopy:clipLabel:itemIdReturn:
v clipboardCopy:itemId:formatName:buffer:privateId:
v clipboardEndCopy:itemId:

For more information, refer to the Common Graphics discussion in Chapter 6.


Common Graphics on page 79.

As an example, consider the following callback method for the Copy menu item:
menuEditCopy
"Private - Process the Edit menu Copy item."
| widget display window itemIdHolder |
(widget := self shell focusWidget) isOleClient
ifFalse: [|self].
display := self shell display.
window := self shell window.
itemIdHolder := ReturnParameter new.
(display
clipboardStartCopy: window
clipLabel: self shell title
itemIdReturn: itemIdHolder) = ClipboardSuccess
ifFalse: [|false].
display
clipboardCopy: window
itemId: itemIdHolder value
formatName: 'Ole Object'
buffer: widget oleObjectDescriptor
privateId: 0.
|(display clipboardEndCopy: window itemId: itemIdHolder value) = ClipboardSuccess

This example first finds the widget that currently has focus in the shells widget
tree. It then queries that widget, using isOleClient, to determine whether it is an
OleClient widget. If it is, the remainder of the method uses standard clipboard
protocol on the shells CgDisplay to copy the special OLE clipboard format
rendering.

Conversely, performing a direct paste operation of an OLE object from the


clipboard requires getting the OLE object format rendering from the clipboard and
giving it to an OleClient widget to create and connect to the referenced OLE object.

Chapter 15. Object Linking and Embedding (OLE) 449


The paste operation uses the standard CgDisplay protocols for retrieving format
renderings from the clipboard:
v clipboardStartRetrieve:
v clipboardRetrieve:formatName:bufferReturn:privateIdReturn:
v clipboardEndRetrieve:

For example, the Paste menu item of the Edit menu might look like:
menuEditPaste
"Private - Process the Edit menu Paste item."
| display window bufferHolder result |
display := self shell display.
window := self shell window.
display clipboardStartRetrieve: window.
result := (display
clipboardRetrieve: window
formatName: 'Ole Object'
bufferReturn: (bufferHolder := ReturnParameter new)
privateIdReturn: ReturnParameter null) == ClipboardSuccess.
display clipboardEndRetrieve: window.
result ifFalse: [|self].
|self paste: bufferHolder value

Here, the value returned from the bufferHolder (a ReturnParameter) is an instance of


OleObjectDescriptor that contains all the data required by an OleClient to recreate
the OLE object that was copied to the clipboard. The paste: method is similar to
menuEditInsertObject: and menuEditPasteSpecial: described above, except that instead
of invoking the dialogs to create and connect the OLE object, the OleClient is asked
to do it from the OleObjectDescriptor:
paste: oleObjectDescriptor
"Private - Paste the OLE object into the receiver."
| client |
oleObjectDescriptor == nil
ifTrue: [|self]. "Nothing to paste"
client := composite
createOleClient: counter printString
argBlock: [:w |
w
x: 50;
y: 50;
width: 200;
height: 200].
client
mappedWhenManaged: false;
manageChild;
pasteFromOleObjectDescriptor: oleObjectDescriptor;
mappedWhenManaged: true

Here, the OleClient does all the work of creating and connecting the OLE object
through its instance method pasteFromOleObjectDescriptor:.

Note: The OLE object format and its rendering are also used for drag and drop
operations on OleClient widgets. The OleClient method oleObjectDescriptor is
used to obtain the source OLE object format rendering for a drag operation,
and the OleClient method pasteFromOleObjectDescriptor: is used to create and
connect an OLE object described in a OLE object rendering obtained from a
drop operation.

The isConnected instance method is used to determine whether an OleClient


is connected to its embedded or linked OLE object.

450 IBM Smalltalk: Programmers Reference


Creating an object verb menu
Verbs are actions that a container can ask an OLE object to perform. The names of
the verbs that an OLE object can execute are registered with the object in the
system registry. All OLE objects have one verb designated as the primary verb,
which in most cases is Edit. Other common verbs are Open, Show, Hide, and Play.

When an OLE object is activated in its container by double-clicking on the


OleClient widget, it is the primary verb that is invoked on the OLE object.
Therefore, if the Edit verb is the primary verb, on activation, the server opens for
editing on the OLE object. Verbs can also be invoked programmatically through the
doVerb: method of OleClient. The parameter to this method is the name of the verb
to execute. For example, a contained OLE object can be activated for editing using
anOleClient doVerb: 'Edit'.

The doVerb: method can also take as a parameter a constant representing the most
common verbs that most OLE objects support. For example, the edit verb could
also be invoked using anOleClient doVerb: XmVERBPRIMARY.

The standard parameter values for the OLE doVerb: include the following:
XmVERBPRIMARY
Open the OLE object for editing
XmVERBSHOW
Show the OLE object
XmVERBOPEN
Open the OLE object for editing in a separate window
XmVERBHIDE
Hide the OLE object
XmVERBUIACTIVATE
Activate the UI for the OLE object
XmVERBINPLACEACTIVATE
Open the OLE object for editing in place
XmVERBDISCARDUNDOSTATE
Close the OLE object and discard the undo state
XmVERBPROPERTIES
Request the OLE object properties dialog

The OLE user interface guidelines suggest that a container provide access to an
objects verbs from a cascade menu off its Edit menu. The container must
dynamically change the contents of the cascade menu depending on which
OleClient widget has the current focus. The guidelines also suggest that the label of
the cascade menu change to reflect the name of the object that the verbs apply to.

To help build a dynamic verb menu, the OleClient class provides these instance
methods:

Chapter 15. Object Linking and Embedding (OLE) 451


clientUserName
Answers a String representing the type of contained OLE object
verbs Answers a collection of strings that are the names of the verbs supported
by the contained OLE object

These methods provide sufficient information to build the dynamic verb menus of
a container application. As an example, suppose the Edit menu of an application is
created as a simple pull-down menu using the following code:
createEditMenu: menuBar
"Private - Create and answer the Edit menu for the receiver."
| types |
types := OrderedCollection new.
types
add: XmPUSHBUTTON;
add: XmPUSHBUTTON;
add: XmPUSHBUTTON;
add: XmPUSHBUTTON;
add: XmPUSHBUTTON;
add: XmSEPARATOR;
add: XmPUSHBUTTON;
add: XmPUSHBUTTON;
add: XmCASCADEBUTTON.
editMenu := menuBar
createSimplePulldownMenu: 'edit'
argBlock: [ :w |
w
buttons: #('Cut' 'Copy' 'Paste' 'Paste Special...'
'Delete Object' '' 'Insert Object...' 'Links...' 'Object');
buttonType: types;
buttonMnemonics: #();
postFromButton: 1].
editMenu
addCallback: XmNsimpleCallback
receiver: [:w :clientData :callData |
| selectors |
selectors := #(menuEditCut menuEditCopy menuEditPaste
menuEditPasteSpecial menuEditDeleteObject menuEditInsertObject
menuEditLinks menuEditObject).
self perform: (selectors at: clientData + 1)]
selector: #value:value:value:
clientData: nil.
editMenu
addCallback: XmNmapCallback
receiver: [:w :clientData :callData |
| widget buttons |
buttons := editMenu children.
widget := self shell focusWidget.
(buttons at: 1) sensitive: widget isOleClient. "Cut"
...
(buttons at: 9)
sensitive: self enableVerbs. "Object"]
selector: #value:value:value:
clientData: nil.
|editMenu

In this example, the last item on the menu is a cascade button that is used to
attach the verb menu to. The verb menu must be disabled if no OleClient widget
has focus. Hence, the XmNmapCallback callback calls the enableVerbs method to
build the verb menu if appropriate. The enableVerbs method first checks for the
existence of an OleClient with focus and, if it finds one, proceeds to build the verb
menu:

452 IBM Smalltalk: Programmers Reference


enableVerbs
"Private - Answer true if the selected embedded object's verbs
are to be enabled."
| widget |
widget := self shell focusWidget.
"Widget with focus is the selected item."
(widget isOleClient not or: [widget isStaticEmbedding])
ifTrue: [
(editMenu children at: 9) labelString: 'Object'.
|false].
"An embedded or linked OLE object is selected, so add verb menu to edit menu."
self addObjectMenu: widget clientUserName
verbs: widget verbs.
|true

The cascading verb menu is built in the addObject:verbs: method as follows:


addObjectMenu: objectType verbs: verbNames
"Private - Add an object menu for object with type name String
objectType containing the verb names in the verbNames Array."
| types labels verbCount objectMenu |
labels := OrderedCollection new: (verbCount := verbNames size) + 2.
types := OrderedCollection new: labels size.
1 to: verbCount do: [ :i |
labels add: (verbNames at: i).
types add: XmPUSHBUTTON].
labels add: 'separator'; add: 'Convert'.
types add: XmSEPARATOR; add: XmPUSHBUTTON.
(editMenu children at: 9) labelString: (objectType, ' Object').
objectMenu := editMenu
createSimplePulldownMenu: 'object'
argBlock: [ :w |
w
buttons: labels asArray;
buttonType: types asArray;
postFromButton: 8].
objectMenu
addCallback: XmNsimpleCallback
receiver: [:w :clientData :callData |
| widget |
widget := self shell focusWidget.
(widget isOleClient and: [verbCount > 0])
ifTrue: [clientData >= verbCount
ifTrue: [self menuEditConvert]
ifFalse:
[widget doVerb: (verbNames at: clientData + 1)]]]
selector: #value:value:value:
clientData: nil.

Notice in this example that one additional menu item, Convert, is attached to the
bottom of the verb menu. This item provides access to the standard OLE Convert
dialog, which allows you to change the display aspect of the OLE object from its

Chapter 15. Object Linking and Embedding (OLE) 453


content display to its icon display and conversely.

This dialog is easily invoked on an OleClient widget through its


promptConvertObject method. For example:
menuEditConvert
"Private - Process the Edit menu Convert item."
| widget |
(widget := self shell focusWidget) isOleClient
ifFalse: [|self].
widget promptConvertObject == true
ifTrue: [changed := true]

The promptConvertObject method answers true if the display aspect of the OLE
object changed. If it did, the OleClient widgets display was already refreshed to
reflect the new aspect.

Invoking the Links dialog


If an OLE container contains linked OLE objects, it should let you edit and modify
those links. OLE provides a standard Links dialog, which lists all the linked OLE
objects in the container. You can:
v Update a link.
v Change a link to a different file.
v Open a linked source file for editing.
v Break a link.

The standard Links dialog is invoked on an OleMainWindow because


OleMainWindow knows about the linked OLE clients in its widget tree. You use the

454 IBM Smalltalk: Programmers Reference


OleMainWindow instance method promptEditLinks to open the Links dialog. For
example, an action method for a Links menu item might simply be the following:
menuEditLinks
"Private - Process the Edit menu Links item."
self mainWindow promptEditLinks

Upon return from promptEditLinks, all requested link edits have been completed,
and any link changes that effect the OLE objects display have also been made.

Note: If the Links dialog is used to break a link to an OLE object, the linked object
automatically reverts to a static embedding. The display of the OLE object is
still visible on its OleClient widget, but it can no longer be activated.

Saving and retrieving an OLE client


An OLE container may be designed to produce documents that consist of native
data and a variety of OLE objects from third-party applications such as Microsoft
Word or Excel. This type of container often persists into a file (perhaps along with
its own native data) the data content (or a link to it) of its contained OLE objects.
At some later time, it reconstructs the document, both native data and contained
OLE object data, from the same file.

A file that supports the storage of both native data and OLE object data is
sometimes called a compound file. The OleFile class supports this kind of
compound-document storage requirement.

An OleFile works seamlessly with an OleClient in the storage and retrieval of its
contained OLE objects data. An OleFile can store multiple OLE clients and, hence,
it requires that each one be given a unique name when put into the file. OLE files
also allow arbitrary application data to be stored with the OleClient in the file.
These attributes of application data are stored in the OleFile under the same name
as the OLE object they are associated with. Finally, an OleFile permits one global,
unnamed set of application data attributes to be stored in the file.

Saving an OLE client


Given a fully qualified path name string, an OleFile can create and open a
compound file for storage through its create: class method. Once opened, an OLE
object can be stored using the saveWidget:named: instance method. For example, all
the embedded or linked OLE objects in an application can be saved using:
saveWidgetsTo: filepath
"Save the applications data to an OleFile specified by the filepath String."
| file widgets |
(file := OleFile create: filePath) == nil
ifTrue: [|false].
widgets := self mainWindow oleWidgets.
widgets do: [ :widget |
file saveWidget: widget named: widget name].
file close

Once an OleFile is created, all OleClient widgets in the application are obtained
from the OleMainWindow through its oleWidgets method. This example assumes that
when each OleClient widget is created, it is given a unique name which it uses as
the unique OleFile storage name.

Note: An OleFile must be closed with its close method before any OLE object data
is committed to the file.

Chapter 15. Object Linking and Embedding (OLE) 455


Retrieving an OLE client
An OleFile can be opened, through its open: class method, on an existing file as
long as the file was originally created as an OleFile. If it was not, the open: fails. An
OLE objects data is retrieved from an OleFile into an existing OleClient widget. The
storageNames instance method of OleFile can be used to obtain all the storage names
from the file so that the OleClient widgets can be created with the same names they
were saved under. Given the storage names, each OleClient can be restored from
the OleFile using its restoreWidget:named: instance method. For example, the OLE
objects saved in the above example can be restored using:
restoreWidgetsFrom: filePath
"Restore the widgets from the file named by the filePath String."
| file names client |
(file := OleFile open: filePath) == nil ifTrue: [|false].
names := file storageNames.
names do: [:name |
client := composite
createOleClient: name
argBlock: nil.
client
mappedWhenManaged: false;
manageChild.
file restoreWidget: client named: name.
client mappedWhenManaged: true].
file close.

In this example, the OleClient widgets are created with default size and position.
The following section describes saving additional attributes of the widgets in the
file.

Saving and restoring data attributes: An arbitrary, named data set can also be
stored in an OleFile. This permits, for example, data attributes of an OleClient to be
stored in the file under the same name as the OleClient. The data set to be saved
must be contained in a ByteArray, which can be filled in a variety of ways,
including by using the Swapper tool. The OleFile instance method
saveAttributes:named: saves a ByteArray of data to the file. For example, the
saveWidgetTo: method above can be enhanced to save each widgets position and
size:
saveWidgetsTo: filepath
"Save the applications data to an OleFile specified by the filepath String."
| file widgets data |
fileName := filepath.
(file := OleFile create: fileName) == nil
ifTrue: [|false].
widgets := self mainWindow oleWidgets.
data := ByteArray new: 16.
widgets do: [ :widget |
data
int32At: 0 put: widget x;
int32At: 4 put: widget y;
int32At: 8 put: widget width;
int32At: 12 put: widget height;
file
saveWidget: widget named: widget name;
saveAttributes: data named: widget name].

file close

Similarly, named data attributes can be restored from an OleFile through the use of
the restoreAttributesNamed: method, which answers a new ByteArray containing the
saved data. Hence, the example method restoreWidgetsFrom: can be rewritten as:

456 IBM Smalltalk: Programmers Reference


restoreWidgetsFrom: filePath
"Restore the widgets from the file named by the filePath String."
| file names client data |
(file := OleFile open: filePath) == nil ifTrue: [|false].
names := file storageNames.
names do: [:name |
data := file restoreAttributesNamed: name.
client := composite
createOleClient: name
argBlock: [:w |
w
x: data int32At: 0;
y: data int32At: 4;
width: data int32At: 8;
height: data int32At: 12].
client
mappedWhenManaged: false;
manageChild.
file restoreWidget: client named: name.
client mappedWhenManaged: true].
file close.

Additional methods of OleFile can be used to save and restore unnamed data sets
to a file. The methods, which also work with byte arrays, are saveAttributes: and
restoreAttributes.

Note: For numeric data, it is often simpler to use ByteArray accessors (such as,
int32At: and int32At:put:) rather than the Swapper tool to fill byte arrays for
use with an OleFile.

Using OLE automation


OLE automation allows an application to expose its function for programmatic use
by another application. An application that makes its function available is called an
OLE automation server. An application that uses that function is called an OLE
automation controller (or client).

OLE automation provides the mechanisms for servers to group their function into
objects, called automation objects, which can then be exposed and used in a
standard way. Automation objects provide access to their function through
methods and access to their attributes or variables through properties (equivalent
to accessor methods for instance variables in Smalltalk). An OLE automation server
exposes its objects for use by registering them in the system registry. If an
automation server provides multiple objects, each one must be registered
separately.

Objects exposed by an OLE automation server can be accessed in Smalltalk


through the OleAutomationObject class. Each automation object provided by a
server is accessed through an instance of OleAutomationObject. Methods of an
OleAutomationObject let you invoke methods and get or set properties of the
automation object.

The steps for creating an OLE automation controller are as follows:


1. Create an OleAutomationObject.
2. Invoke methods on the OleAutomationObject.
3. Get and set properties of the OleAutomationObject.
4. Release the OleAutomationObject when it is no longer needed.

Chapter 15. Object Linking and Embedding (OLE) 457


Creating OLE Automation Objects
These are the ways to obtain OleAutomationObjects:
v Create them directly using their ProgId, or client name, from the system registry.
v Request them from another OleAutomationObject.
v Request them from an existing OleClient that contains an embedded or linked
OLE object.
v Request them from an existing OleControl (see Using OLE custom controls
(OCXs) on page 460).

You use the class method createObject: to instantiate an OleAutomationObject using a


ProgId from the system registry. For example, Word Basic, the only OLE
automation object exposed by Microsoft Word, has a ProgId of Word.Basic, so an
instance of that object is created using:
| wordbasic |
wordbasic := OleAutomationObject createObject: 'Word.basic'

Unlike Microsoft Word, many OLE automation servers provide a number of


automation objects organized as networks of interrelated objects. As such, one OLE
automation object may reference another. The references to other automation
objects are maintained as properties of the object. Values of an OLE automation
objects properties are obtained by their name or DISPID. Support for property
access by an OleAutomationObject is through its propertyAt: instance method, and,
hence, additional OLE automation objects are obtained through this method. For
example, the top-level application object of Microsoft Excel is accessed using:
| excel application |
excel := OleAutomationObject createObject: 'Excel.Sheet'.
application := excel propertyAt: 'Application'.

Many, but not all, OLE document servers also provide OLE automation objects. An
OleClient, through its automationObject instance method, may answer the
automation object of its embedded or linked OLE object. For example, if a
Microsoft Word object is embedded in a container, its Word Basic automation object
is obtained using:
| word wordDispatch wordApplication wordBasic |
word := mainWindow
createOleClient: 'word'
argBlock: [:w |
w
clientType: XmEMBEDDED;
clientName: 'Word.Document'].
word isConnected ifFalse: [word destroyWidget. |nil].
word manageChild.
wordDispatch := word automationObject.
wordApplication := wordDispatch propertyAt: 'Application'.
wordBasic := wordApplication propertyAt: 'WordBasic'.

Invoking methods of OLE automation objects


Methods of OLE automation objects are invoked by their unique name or DISPID.
An OleAutomationObject has instance methods that are used to call a method of an
OLE automation object. The methods are invoke:withArguments: and
invoke:withArguments:returnType:.

Both methods take as a first parameter the name (or DISPID) of the method to
invoke, and as a second parameter an array of parameters to be passed to the
method.

458 IBM Smalltalk: Programmers Reference


The invoke:withArguments: returns the result of the OLE method sent, or nil if there
was an error.

The value returned from invoke:withArguments:returnType: depends on the value of


the returnType: parameter, as described below. A false value returnType: is required
because some automation servers, such as Microsoft Word, have methods that fail
unless their return values are not requested. The values of returnType include the
following:
true Answers the return value of the OLE method
false Ignores the return value of the OLE method

Note: If an error is indicated with a nil return from one of the OLE automation
objects invoke methods, a String describing the error may be obtained by
sending the lastErrorString message to the OleAutomationObject.

Smalltalk objects returned from an OleAutomationObject invoke method are


converted from their OLE types to equivalent Smalltalk types. Likewise, Smalltalk
objects passed as parameters to an invoke method are converted to their OLE
equivalent types based on their Smalltalk class. These conversions are supported
only for the simple Smalltalk objects listed below. The Smalltalk classes supported
for automatic conversion to and from OLE types include the following:
v Integer
v Float
v String
v Boolean
v OleAutomationObject

Note: If a parameter to one of the invoke methods cannot be represented in one of


the supported types named above, an initialized instance of OSVariantarg
may be used. Likewise, a return value of an invoke method is either one of
the supported types or an instance of OSVariantarg.

As an example of using the invoke methods on an OleAutomationObject, consider


the following code fragment that opens Microsoft Word, inserts some text, moves
the text insertion point, and selects the text:
| wordBasic text |
wordBasic := OleAutomationObject createObject: 'Word.basic'.
text := ' A small amount text for Word.'.
wordBasic
invoke: 'FileNew' withArguments: #();
invoke: 'CharRight' withArguments: #(1);
invoke: 'Insert' withArguments: (Array with: text) returnType: false;
invoke: 'ExtendSelection' withArguments: #() returnType: false;
invoke: 'CharLeft' withArguments: (Array with: text size).

Getting and setting properties


Properties of OLE automation objects are also accessed by their unique name or
DISPID. The OleAutomationObject class has these property-accessor instance
methods:
propertyAt:
Returns the value of a named property
propertyAt:put:
Sets the value of a named property

These methods function like the invoke methods with respect to parameters and
return values.

Chapter 15. Object Linking and Embedding (OLE) 459


As an example of using the property methods on an OleAutomationObject, consider
the following code fragment that opens Microsoft Excel, makes it visible, changes
the default file path, and opens a file:
| excel application workBooks |
excel := OleAutomationObject createObject: 'Excel.Sheet'.
"Get the root Excel Application object."
application := excel propertyAt: 'Application'.
"Make Excel visible."
application propertyAt: 'Visible' put: true.
"Set the default file path."
application propertyAt: 'DefaultFilePath' put: 'c:\budget\'.
"Get the Excel Workbooks object."
workBooks := application propertyAt: 'Workbooks'.
"Open an existing Excel file (.XLS)."
workBooks invoke: 'Open' withArguments: #('budget96.xls').
...
application invoke: 'Quit' withArguments: #()

Releasing an OLE automation object


Each instance of an OleAutomationObject has some operating system resources
associated with it that must be freed when they are no longer required. Hence,
when an OLE automation controller is finished using these objects, it must release
those resources. The OleAutomationObject instance method releaseObject performs
this function. For example, in the previous example the three Microsoft Excel OLE
automation objects should be released immediately after the Quit method is
invoked:
...application invoke: 'Quit' withArguments: #()
workBook releaseObject.
application releaseObject.
excel releaseObject.

Note: After the releaseObject message is sent, the OleAutomationObject is no longer


connected to the OLE automation server and, as such, it cannot be used. To
connect back to the same server, a new instance of OleAutomationObject must
be created. After a releaseObject message is sent, it is good practice to assign
nil values to all Smalltalk variables that were referencing the
OleAutomationObject.

Using OLE custom controls (OCXs)


An OLE custom control (OCX) is an embedded OLE object that is activated in
place. Unlike OLE objects, however, OCXs are manipulated programmatically by
their containers through standard OLE automation protocols. OCXs are most often
implemented as DLLs, which are directly loaded into the address space of their
container.

An OCX:
v Has an OLE automation element with properties that are retrieved and
modified, and methods that are invoked
v Obtains information from its container to initialize its own state; that is, it
gathers the ambient properties from its container
v Notifies its container, through events, when a user action causes its state to
change or a function to be performed.

Essentially, an OCX is an in-place-activated OLE object. On the other hand, an


OCX container is a standard OLE container that supports visual editing.

460 IBM Smalltalk: Programmers Reference


An OCX container:
v Provides in its user interface the site for the OCXs user interface.

Note: Some OCXs may not have a user interface component; it is not a
requirement.
v Sets properties and makes action requests, through OLE automation, of an OCX
v Provides an ambient-properties handler (callback) that responds to
ambient-property requests from an OCX
v Provides a set of event handlers (callbacks) that respond to event notifications
from an OCX.

The steps to create an OLE container that contains OCXs are:


1. As part of the initialization for the user-interface widget hierarchy, create an
OleMainWindow.
2. Implement methods to create OleControl widgets and the OCX objects that are
to appear within their borders.
3. Implement the ambient-property handler.
4. Use OLE automation aspects of an OCX to set or get properties and invoke
methods on the OCX.
5. Implement the event handler callbacks.

Creating the OLE main window


Since applications using OCXs are OLE containers, they must also use the
OleMainWindow class rather than CwMainWindow. Even though OCXs do not use
menus or do not require additional window space for status bars and so on, they
still require OleMainWindow to support in-place activation of the contained OCXs
user interface. As in Creating an OLE main window on page 443, an
OleMainWindow is simply created using:
createMainWindow: shell
"Private - Create and answer the main window for the receiver."
|(shell
createOleMainWindow: 'main'
argBlock: [:w | w hostName: 'Container']) manageChild

Since OCXs do not use menus, no menu groups need be registered with the
OleMainWindow (unless of course, there are OleClient widgets used for embedded
or linked OLE objects in the same application).

Creating OCXs
An OleControl widget provides an OLE container the place to put OCXs. OLE
controls are primitive widgets and, as such, they conform to all standard
widget-geometry management. An OleControl widget contains the connections to
an OCX and transparently manages all low-level OLE interaction with that object.
In addition, an OleControl widget provides an OLE automation interface for its
contained OCX to be used to set and get OCX properties and to invoke OCX
methods.

OleControl widgets are created and managed like other primitive widgets. Unlike
OleClient widgets, however, the only way to create and connect an OCX to an
OleControl widget is through its registered name. This is the same as the creation
of OLE clients using the clientName resource, which is based on the OCXs
registered programmatic identifier, or ProgId.

Chapter 15. Object Linking and Embedding (OLE) 461


Since OLE controls are also created as child widgets of any non-primitive widget
(that is, any CwComposite), there is an additional widget-creation convenience
method implemented in CwWidget, called createOleControl:argBlock:. The creation
block passed to this method must not only specify the clientName resource (through
the clientName: method) but also the ambient-property handler, or callback, to be
used by the contained OCX. This ambient-property handler is specified through
the OleControl instance methods ambientPropertyReceiver: and
ambientPropertySelector:.

The ambient-property handler must be specified when the OleControl widget is


created, so that the OCX has access to the containers properties during its
initialization. As an example, consider a container that uses a track bar OCX that is
created using:
createTrackBar: workWindow
"Private - Create a track bar control."
trackBar := workWindow
createOleControl: 'the control'
argBlock: [:w |
w
clientName: 'TRACKBAR.TrackbarCtrl.1';
ambientPropertyReceiver: self;
ambientPropertySelector: #dispidAmbientPropertyAt:].
trackBar manageChild.

Implementing an ambient-property handler


Ambient properties are used by a container to give an OCX information about its
surroundings, such as the background color of a form, the current font, or the
locale ID of the containers user interface.

Each ambient property is identified by a unique DISPID, which an OCX uses to


specify which ambient property it wants from its container. To support the
integration of OCXs with containers, OLE defines a set of standard
ambient-property DISPIDs. This set is encoded into the Smalltalk pool dictionary
PlatformConstants as follows:

Ambient Property DISPID Class Specifies


DispidAmbientBackcolor Integer The color of the interior of an OCX (in
RGB values)
DispidAmbientDisplayname String the name the OCX should display for
itself in error messages
DispidAmbientDisplayasdefault Boolean Whether a button OCX should draw
itself with a thicker frame (for
button-like OCXs only)
DispidAmbientForecolor Integer The color of the display of text and
graphics in an OCX (in RGB values)
DispidAmbientLocaleid Integer The ID of the UI locale
DispidAmbientMessagereflect Boolean Whether the container wants to receive
MS Windows messages
DispidAmbientSupportsmnemonics Boolean Whether the container processes
mnemonics
DispidAmbientShowgrabhandles Boolean Whether the OCX should show grab
handles when in-place activated
DispidAmbientShowhatching Boolean Whether an OCX should show a hatch
border when in-place activated

462 IBM Smalltalk: Programmers Reference


DispidAmbientScaleunits String The name of the units by the container
DispidAmbientTextalign Integer How text should be aligned in an OCX:
0 General (numbers to the right,
text to the left)
1 Left
2 Center
3 Right
4 Fully justified
DispidAmbientUsermode Boolean Whether the container is in design
mode (false) or run mode (true)
DispidAmbientUidead Boolean Whether the container is in a mode in
which OCXs should ignore user input

Ambient-property handlers are implemented as methods that take a single


parameter, which is the DISPID of the ambient property being requested by the
OCX. OCXs may not request all possible ambient properties but only those they
are interested in. Likewise, a container may not necessarily be able to respond to
all ambient property requests. The ambient property handler answers the value of
the requested property if it can; otherwise, it answers a nil value to inform the
OCX that the property is not available. For example, the ambient-property-handler
method dispidAmbientPropertyAt: in the above example might be implemented as
follows:
dispidAmbientPropertyAt: dispid
"Private - Handle ambient properties."
DispidAmbientShowgrabhandles == dispid ifTrue: [|false].
DispidAmbientShowhatching == dispid ifTrue: [|false].
DispidAmbientLocaleid == dispid ifTrue: [|LocaleUserDefault].
DispidAmbientUidead == dispid ifTrue: [|false].
DispidAmbientUsermode == dispid ifTrue: [|false].
|nil

Here, the constant LocaleUserDefault comes from the pool dictionary


PlatformConstants.

Note: A class that implements OLE-container behavior should declare


PlatformConstants as pool dictionaries.

Using automation aspects of an OCX


OCXs are also OLE automation objects; that is, they have properties that can be set
and retrieved, and they have functions that can be invoked on behalf of their
containers.

To support its OLE automation aspects, the OleControl class has the same
automation protocols as the OleAutomationObject class, namely:
v propertyAt:
v propertyAt:put:
v invoke:withArguments:
v invoke:withArguments:returnType:

Since complex OCXs are often composed of many distinct objects, some of these
methods may themselves return OLE automation objects.

The properties that an OCX defines are characteristic of the OCX. These
characteristics can include the font used, the background color, or the caption.
Since so many of these characteristics are common to so many OCXs, OLE has

Chapter 15. Object Linking and Embedding (OLE) 463


defined a set of standard property DISPIDs for them. A set of constants for these
DISPIDs is also defined in the PlatformConstants pool dictionary. (See the Standard
Property DISIDs in the table below.)

As an example of setting properties, the following code fragment initializes the


track bar OCX created in the previous example:
trackBar
propertyAt: 'SliderStyle' put: 0 "Enable selection range";
propertyAt: 'SelectionStart' put: 1;
propertyAt: DispidEnabled put: true;
propertyAt: 'SelectionEnd' put: 100;
invoke: 'SetTicFrequency' withArguments: (Array with: 1 with: 5)

Standard Property DISPID Class Specifies


DispidBackcolor Integer The color of the interior of the OCX (in RGB
values)
DispidBackstyle Integer Whether an OCX is transparent (0) or opaque
(1)
DispidForecolor Integer The color of the display of text and graphics in
an OCX (in RGB values)
DispidEnabled Boolean Whether the OCX can receive the focus
DispidText String The string to be displayed in a text box, list
box, or combo box
DispidCaption String The string to be displayed in, or next to, the
OCX
DispidRefresh None Force repainting the OCX synchronously if the
OCX has a window, otherwise asynchronously
DispidDoclick None Simulate the button being clicked by the user
(for button-like OCXs)
DispidAboutbox None Pop up a modal About dialog for the OCX

Note: The font property DISPID DispidFont is not supported. Instead, use the
fontList: instance method of OleControl to set a font into an OCX.

Implementing event handlers


Every OCX has a set of event notifications that it can send to its container. OCX
events encompass both event and callback notions of Common Widgets. That is,
OCX events can be fine-grained (such as mouse-clicks or key-presses), or they can
be notifications of some higher-level action (such as a Cancel button being
selected). The events that a particular OCX support are normally described in the
OCXs type library or in the documentation that is shipped with it. Each event that
is supported by an OCX is identified by a unique name and a unique DISPID.

An OCX container must register a method for each OCX event it will handle. The
name of the event, the selector of the event-handler method, and the object that
receives the notification are registered with an OleControl through its
addOleEventHandler:receiver:selector: instance method. For example, the track bar
OCX sends an event called Change whenever the user moves its slider. The
handler for this event is registered using:
trackBar
addOleEventHandler: 'Change'
receiver: self selector: #updateValue.

464 IBM Smalltalk: Programmers Reference


Here, the Change event passes no parameters to the event handler, so its
implementation must request the current Value property from the OCX. For
example:
updateValue
"Update the track bar's label with the current value of the track bar."
self updateTrackLabel: (trackBar propertyAt: 'Value')

Many OCX events pass one or more parameters to their container along with the
event notification. Therefore, the event-handler method selectors must
accommodate the required number of parameters. For example, an OCXs
mouse-down event passes four parameters to its containers event handler:
v The mouse button that was pressed
v The state of the shift key
v The mouse pointer X location
v The mouse pointer Y location.

Hence, a handler for this event is registered for the track bar OCX using:
trackBar
addOleEventHandler: 'MouseDown'
receiver: self selector: #eventMouseDown:shift:x:y:.

where the implementation of this event handler is:


eventMouseDown: button shift: shift x: nX y: nY
"Private - Processes the OLE Track bar MouseDown event."
...

An OLE event handler is deregistered from an OleControl through its instance


method removeOleEventHandler:receiver:selector:.

There is a set of predefined OLE event names and DISPIDs for the base mouse and
keyboard events that are used by most OCXs. These predefined events overlap the
Common Widgets event masks that are inherited by OleControl. As a convenience,
OleControl registers handlers for the corresponding OLE predefined events and
automatically reroutes them through the Common Widgets event-handler
mechanism. For example, the mouse-down OLE event handler registered in the
previous example can be rewritten to use a Common Widgets event handler:
trackBar
addEventHandler: ButtonPressMask
receiver: self
selector: #value:clientData:callData:
clientData: nil.

OleControl registers OLE event handlers for these predefined events:


v KeyDown
v KeyUp
v MouseDown
v MouseMove
v MouseUp

If a container registers for one of the predefined OLE events using


addOleEventHandler:receiver:selector:, its handler supersedes the OleControl widgets
registered predefined event.

Predefined DISPID User action Parameters Description


OLE event (in order)
name

Chapter 15. Object Linking and Embedding (OLE) 465


Click DispidClick Presses and releases None
a mouse button
over the OCX. For
some OCXs, the
event is sent when
the value of the
OCX changes.
DblClick DispidDblclick Double-clicks in the None
OCX
KeyDown DispidKeydown Presses a key when Key Code The key code for
the OCX has focus Shift the key pressed A
bit mask detailing
the state of the
Ctrl, shift, and
Alt keys
KeyUp Releases a key Key Code See KeyDown
when the OCX has Shift
focus
MouseDown DispidMousedown Presses a mouse Button Shift A bit mask
button while over XY identifying which
an OCX button is down A
bit mask detailing
the state of the
Ctrl, shift, and
Alt keys The
current X location
of the mouse The
current Y location
of the mouse
MouseMove DispidMousemove Moves the mouse Button Shift See MouseDown
over an OCX XY
MouseUp DispidMouseup Releases the mouse Button Shift See MouseDown
button over an OCX X Y

Wrapping OCXs with extended widgets


The Common Widgets Extended Widgets framework can be used to wrapper an
OleControl so it can be used in a more familiar way. That is, Smalltalk accesssors
are used to wrap the OleControl property messages (propertyAt: and propertyAt:put:)
and invoke message (invoke:withArguments:), and the Common Widgets callback
style is used for all the OLE events of interest.

For a reference on building Extended Widgets, see the Chapter 8. Extended


Widgets on page 233. The main approach is to:
1. Make a subclass CwExtentedPrimitive.
2. Create an instance of OleControl in the Extended Widgets
createPrimaryWidget:parent:argBlock: method and register an ambient-property
handler. For example:
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
createOleControl: 'the control'
argBlock: [:w | w

466 IBM Smalltalk: Programmers Reference


clientName: 'TRACKBAR.TrackbarCtrl.1';
ambientPropertyReceiver: self;
ambientPropertySelector: #dispidAmbientPropertyAt:].
3. Register OLE event handlers and associate them with Common Widgets
callbacks in the initializeAfterCreate method. For example:
initializeAfterCreate
"Private - Perform any widget-specific post-create initialization."
super initializeAfterCreate.
self primaryWidget
addOleEventHandler: 'Change'
receiver: self
selector: #callValueChangedCallback
4. Implement widget-specific methods, such as:
clearSelection
"Clears the slider's selection range."
|self primaryWidget
invoke: 'ClearSelection'
withArguments: #()
5. Implement accessors for resources, such as:
lineSize: resourceValue
"Set the value of the XmNlineSize resource.
Resource type: Integer
Default setting: 10
Resource access: CSG
Description: Specifies the line size. This is the amount
that the slider moves when receiving a key press."
self primaryWidget propertyAt: 'LineSize' put: resourceValue
lineSize
"Answer the value of the XmNlineSize resource.
Resource type: Integer
Default setting: 10
Resource access: CSG
Description: Specifies the line size. This is the amount
that the slider moves when receiving a key press."
self primaryWidget propertyAt: 'LineSize'

Using licensed OCXs


Many commercially available OCXs limit developer usage through licensing. With
licensing, an OCX fails creation unless it is provided with the correct license key.
The vendor of the OCX provides the license key in a file. The license must be
obtained from the license file at development time and then cached for use during
the runtime creation of the OCX.

To support the use of licensed OCXs, the OleControl class has the class method
getLicenseKey:, which is used at development time to obtain the license key. This
method takes as a parameter the ProgId of the OCX and answers a string
representing the license key. The license key is then cached in the Smalltalk image
(for example, in a class variable of an Extended Widgets wrapper of the OCX) and
used in the subsequent creation of the OleControl widget.

For example, if the track bar OCX is licensed, its license key is obtained using the
following code:
getLicenseKey
"Answer a string that can be used to license the receiver,
or nil if either the OCX does not require licensing
or the OCX does support licensing but cannot be propagated."
LicenseKey := OleControl getLicenseKey: 'TRACKBAR.TrackbarCtrl.1'

LicenseKey is a class variable of the Extended Widgets class.

Chapter 15. Object Linking and Embedding (OLE) 467


Then the createPrimaryWidget:parent:argBlock: method is changed to be as follows:
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
createOleControl: 'the control'
argBlock: [:w | w
clientName: 'TRACKBAR.TrackbarCtrl.1';
licenseKey: LicenseKey;
ambientPropertyReceiver: self;
ambientPropertySelector: #dispidAmbientPropertyAt:].

Note: The licenseKey: method must be used in the create block of the OleControl.

468 IBM Smalltalk: Programmers Reference


Appendix A. Widget resources and callbacks
This appendix provides a detailed listing of the resources and callbacks for each
class in the Common Widgets subsystem.

Only set resource methods are shown for each class. Get methods use the same
format without the colon. Create/Set/Get (CSG) access designation and default
value are listed for each resource and call. For more on resource designation, see
Resources on page 141.

Call list set methods are also shown, even though the callbacks are usually added
using addCallback:receiver:selector:clientData:, and are not using a callback list set
method. These methods end in Callback:.

Resource methods in this font indicate an overridden default value.


Table 60. CwArrowButton resource and callback methods
activateCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the ArrowButton is activated.

CallData type is CwAnyCallbackData.


armCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the receiver is armed.

CallData type is CwAnyCallbackData.


arrowDirection: CSG XmARROWUP Sets the arrow direction.
disarmCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the receiver is disarmed.

CallData type is CwAnyCallbackData.


CwArrowButton also inherits resource and callback methods from CwPrimitive, CwBasicWidget and CwWidget. Consult
the resource tables of these classes for more information.

Table 61. CwBasicWidget resource and callback methods


CwBasicWidget inherits resource and callback methods from classCwWidget. Consult the resource tables of this class
for more information.

Table 62. CwBulletinBoard resource and callback methods


autoUnmanage: CG true Controls whether or not BulletinBoard is
automatically unmanaged after a button is
activated.
buttonFontList: CSG dynamic Specifies the font list used for BulletinBoards
button children (PushButtons, ToggleButtons).
cancelButton: SG nil Specifies the widget that is the Cancel button.
defaultButton: SG nil Specifies the widget that is the default
button.
defaultPosition: CSG nil Controls whether or not BulletinBoard is
automatically positioned by its parent.
dialogStyle: CSG dynamic Indicates the dialog style associated with
BulletinBoard.

Copyright IBM Corp. 1994, 2000 469


Table 62. CwBulletinBoard resource and callback methods (continued)
dialogTitle: CSG nil Specifies the dialog title.
labelFontList: CSG dynamic Specifies the font list used for BulletinBoards
Label children (Labels).
mapCallback: C OrderedCollection new Specifies the list of callbacks that is called
only when the parent of the BulletinBoard is
a DialogShell. In this case, this callback list is
invoked when the BulletinBoard widget is
managed.

CallData type is CwAnyCallbackData.


marginHeight: CSG 10 Specifies the minimum spacing in pixels
between the top or bottom edge of
BulletinBoard and any child widget.
marginWidth: CSG 10 Specifies the minimum spacing in pixels
between the left or right edge of
BulletinBoard and any child widget.
noResize: CSG false Controls whether or not resize controls are
included in the window manager frame
around the dialog.
resizePolicy: CSG XmRESIZEANY Controls the policy for resizing BulletinBoard
widgets.
textFontList: CSG dynamic Specifies the font list used for BulletinBoards
Text children.
unmapCallback: CSG OrderedCollection new Specifies the list of callbacks that is called
only when the parent of the BulletinBoard is
a DialogShell. In this case, this callback list is
invoked when the BulletinBoard widget is
unmapped.

CallData type is CwAnyCallbackData.


CwBulletinBoard also inherits resource and callback methods from CwComposite, CwBasicWidget and CwWidget.
Consult the resource tables of these classes for more information.

Table 63. CwCascadeButton resource and callback methods


cascadingCallback: C OrderedCollection new Specifies the list of callbacks that is called
just prior to the mapping of the submenu
associated with CascadeButton.

CallData type is CwAnyCallbackData.


marginBottom: CSG dynamic Overrides default value from CwLabel.
marginLeft: CSG dynamic Overrides default value from CwLabel.
marginRight: CSG dynamic Overrides default value from CwLabel.
marginTop: CSG dynamic Overrides default value from CwLabel.
subMenuD: CSG nil Specifies the widget ID for the Pulldown
MenuPane to be associated with this
CascadeButton. The cascade button and its
associated pulldown menu must have the
same (or common) parent.
traversalOn: CSG true Overrides default value from CwLabel.
CwCascadeButton also inherits resource and callback methods from CwLabel, CwPrimitive, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

470 IBM Smalltalk: Programmers Reference


Table 64. CwComboBox resource and callback methods
activateCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user press

CallData type is CwAnyCallbackData.


comboBoxType: CG XmDROPDOWN Specifies the style of combo box.
editable: CSG false Indicates whether the user can edit the text
string when set to true.
focusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before Text has accepted input focus.

CallData type is CwAnyCallbackData.


fontList: CSG dynamic Specifies the font list to be used for
CwComboBox.
itemCount G 0 Specifies the total number of items in the list.
items: CSG nil An array of Strings that are to be displayed
as the list items.
losingFocusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before Text loses input focus.

CallData type is CwAnyCallbackData.


maxLength: CG dynamic Specifies the maximum length of the text
string that can be entered into text from the
keyboard.
modifyVerifyCallback: C OrderedCollection new Specifies the list of callbacks that is called
before text is deleted from or inserted into
Text.

CallData type is CwTextVerifyCallbackData.


navigationType: CSG XmTABGROUP Overrides default value from CwPrimitive
popdownCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the item list disappears.

CallData type is CwAnyCallbackData


popupCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the item list appears.

CallData type is CwAnyCallbackData.


selectedItems CSG nil An array of 0 or 1 Strings that represents the
item currently selected.
singleSelectionCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user confirms a change to the
combo box value.

CallData type is CwComboBoxCallbackData.


valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is called
after text is deleted from or inserted into
Text.

CallData type is CwAnyCallbackData.


verifyBell: CSG true Specifies whether the bell should sound
when the verification returns without
continuing the action.

Appendix A. Widget resources and callbacks 471


Table 64. CwComboBox resource and callback methods (continued)
visibleItemCount: CG 5 Specifies the number of items that can fit in
the visible space of the list work area.
CwComboBox also inherits resorce and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult
the resource tables of these classes for more information.

Table 65. CwComposite resource and callback methods


backgroundColor: CSG dynamic Specifies the background drawing color.
exposeCallback: C OrderedCollection new Specifies the list of callbacks that is
called when widget receives an exposure
event.

CallData type is
CwDrawingCallbackData.
focusCallback: C OrderedCollection new Specifies the list of callbacks that is
called after a widget has accepted input
focus.

CallData type is CwAnyCallbackData.


foregroundColor: CSG dynamic Specifies the foreground drawing color.
helpCallback: C OrderedCollection new Specifies the list of callbacks that is
called when the help key sequence is
pressed.

CallData type is CwAnyCallbackData.


interceptExposeCallback: C OrderedCollection new Specifies the list of callbacks that is
called when any area of the receiver or
its children is exposed.

CallData type is
CwDrawingCallbackData.
losingFocusCallback: C OrderedCollection new Specifies the list of callbacks that is
called after widget loses input focus.

CallData type is
CwDrawingCallbackData.
navigationType: CSG XmTABGROUP Specifies the value of the
XmNnavigationType resource.
strictClipping: CG self parent strictClipping Some platforms show a dramatic speed
improvement in the time to draw
windows containing many widgets if
they are not required to ensure certain
clipping constraints.
traversalOn: CSG true Specifies if traversal is activated for this
widget.
CwComposite also inherits resource and callback methods from CwBasicWidget and CwWidget. Consult the resource
tables of these classes for more information.

Table 66. CwCompositeBox resource and callback methods


cancelLabelString: CSG Cancel Specifies the string label for the cancel
button.
helpLabelString: CSG Help Specifies the string label for the help button.

472 IBM Smalltalk: Programmers Reference


Table 66. CwCompositeBox resource and callback methods (continued)
minimizeButtons: CSG false Sets the buttons to the width of the widest
button and height of the tallest button if
false.
okCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user clicks on the OK Button.

CallData type is CwAnyCallbackData.


okLabelString: CSG OK Specifies the string label for the OK button.
CwCompositeBox also inherits resource and callback methods from CwBulletinBoard, CwComposite, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

Table 67. CwDialogShell resource and callback methods


mwmInputMode: CSG -1 Specifies the input mode flag.
CwDialogShell also inherits resource and callback methods from CwTransientShell, CwWMShell, CwShell,
CwBasicWidget, and CwWidget. Consult the resource tables of these classes for more information.

Table 68. CwDrawingArea resource and callback methods


inputCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the DrawingArea processes a keyboard
or mouse event (key or button, up or down).

CallData type is CwDrawingCallbackData.


marginHeight: CSG 10 Specifies the minimum spacing in pixels
between the top or bottom edge of
DrawingArea and any child widget.
marginWidth: CSG 10 Specifies the minimum spacing in pixels
between the left or right edge of
DrawingArea and any child widget.
resizePolicy: CSG XmRESIZEANY Specifies the resize policy for the widget.
CwDrawingArea also inherits resource and callback methods from CwComposite, CwBasicWidget, and CwWidget.
Consult the resource tables of these classes for more information.

Table 69. CwDrawnButton resource and callback methods


activateCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the widget is selected.

CallData type is CwDrawingCallbackData.


armCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is armed.

CallData type is CwAnyCallbackData.


disarmCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is disarmed.

CallData type is CwAnyCallbackData.


exposeCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the widget receives an exposure event.

CallData type is CwAnyCallbackData.


labelString: CSG Overrides default value from CwLabel.

Appendix A. Widget resources and callbacks 473


Table 69. CwDrawnButton resource and callback methods (continued)
focusCallback: C OrderedCollection new Specifies the list of callbacks that is called
after widget has accepted input focus.

CallData type is CwAnyCallbackData.


losingFocusCallback: C OrderedCollection new Specifies the list of callbacks that is called
after widget loses input focus.

CallData type is CwAnyCallbackData.


pushButtonEnabled: CSG false Enables or disables the three-dimensional
shadow drawing as in PushButton.
traversalOn: CSG true Overrides default value from CwLabel.
CwDrawnButton also inherits resource and callback methods from CwLabel, CwPrimitive, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

Table 70. CwForm resource and callback methods


fractionBase: CSG 100 Specifies the denominator used in calculating
the relative position of a child widget using
XmATTACHPOSITION constraints.
horizontalSpacing: CSG 0 Specifies the offset for right and left
attachments.
marginHeight: CSG 0 Overrides default value from CwBulletinBoard
marginWidth: CSG 0 Overrides default value from CwBulletinBoard
rubberPositioning: CSG false Indicates the default attachment for a child of
the Form.
verticalSpacing: CSG 0 Specifies the offset for top and bottom
attachments.
CwForm also inherits resource and callback methods from CwBulletinBoard, CwComposite, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

Table 71. CwFrame resource and callback methods


fontList: CSG dynamic Specifies the font of the text used in the
widget.
labelString: CSG Specifies a text string that is the title of the
receiver.
marginHeight: CSG 0 Specifies the padding space on the top and
bottom sides between the child of Frame and
the Frames shadow drawing.
marginWidth: CSG 0 Specifies the padding space on the left and
right sides between the child of Frame and
the Frames shadow drawing.
shadowType: CSG XmSHADOWDEFAULT Describes the drawing style for Frame.
CwFrame also inherits resource and callback methods from CwComposite, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

Table 72. CwLabel resource and callback methods


accelerator: C nil Specifies the accelerator on a button widget
in a menu, which activates a visible or
invisible button from the keyboard.

474 IBM Smalltalk: Programmers Reference


Table 72. CwLabel resource and callback methods (continued)
acceleratorText: CSG Specifies the text displayed for the
accelerator.
alignment: CSG XmALIGNMENTCENTER Specifies the label alignment for text or
pixmap.
fontList: CSG dynamic Specifies the font of the text used in the
widget.
labelIcon: CSG XmUNSPECIFIEDICON Specifies the icon when XmNlabelType is
XmICON.
labelInsensitiveIcon: CSG XmUNSPECIFIEDICON Specifies an icon used as the button face if
XmNlabelType is XmICON and the button is
insensitive.
labelInsensitivePixmap: CSG XmUNSPECIFIEDPIXMAP Specifies a pixmap used as the button face if
XmNlabelType is XmPIXMAP and the button
is insensitive.
labelPixmap: CSG XmUNSPECIFIEDPIXMAP Specifies the pixmap when XmNlabelType is
XmPIXMAP.
labelString: CSG dynamic Specifies the label string when the
XmNlabelType is XmSTRING. If labelString is
not explicitly set, the widgets name is used
by default.
labelType: CSG XmSTRING Specifies the label type.
marginBottom: CSG 0 Specifies the amount of spacing that is to be
left, after the bottom margin
(XmNmarginHeight) of the widget, but
before the label is drawn.
marginHeight: CSG 2 Specifies the amount of space between the
bottom edge of the top shadow and the label,
and the top edge of the bottom shadow and
the label.
marginLeft: CSG 0 Specifies the amount of spacing that is to be
left, after the left margin (XmNmarginWidth)
of the widget, but before the label is drawn.
marginRight: CSG 0 Specifies the amount of spacing that is to be
left, after the right margin
(XmNmarginWidth) of the widget, but before
the label is drawn.
marginTop: CSG 0 Specifies the amount of spacing that is to be
left, after the top margin (XmNmarginHeight)
of the widget, but before the label is drawn.
marginWidth: CSG 2 Specifies the amount of space between the
right edge of the left shadow and the label,
and the left edge of the right shadow and the
label.
mnemonic: CSG nil Provides the user with alternative means for
selecting a button.
recomputeSize: CSG true Specifies a Boolean value that indicates
whether or not the widget always attempts to
be big enough to contain the label.
traversalOn: CSG false Overrides default value form CwPrimitive
CwLabel also inherits resource and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

Appendix A. Widget resources and callbacks 475


Table 73. CwList resource and callback methods
borderwidth: CSG 1 Overrides a default value from
CwWidget.
browseSelectionCallback: C OrderedCollection new Specifies a list of callbacks that is
called when an item is selected in the
browse selection mode.

CallData type is CwListCallbackData.


defaultActionCallback: C OrderedCollection new Specifies a list of callbacks that is
called when an item is double clicked.

CallData type is CwListCallbackData.


extendedSelectionCallback: C OrderedCollection new Specifies a list of callbacks that is
called when items are selected using
the extended selection mode.

CallData type is CwListCallbackData.


fontList: CSG dynamic Specifies the font list associated with
the list items.
itemCount CSG 0 Specifies the total number of items.
items: CSG nil An array of Strings that are to be
displayed as the list items.
multipleSelectionCallback: C OrderedCollection new Specifies a list of callbacks that is
called when an item is selected in
multiple selection mode.

CallData type is CwListCallbackData.


navigationType: CSG XmTABGROUP Overrides default value from
CwPrimitive.
scrollHorizontal: CG dynamic Indicates that a horizontal scroll bar is
desired for this list. Ignored on
platforms where the feature is not
configurable.
selectedItemCount CSG 0 Specifies the number of strings in the
selected items list.
selectedItems: CSG nil An array of Strings that represents the
list items that are currently selected,
either by the user or the application.
selectionPolicy: CSG XmBROWSEELECT Defines the interpretation of the
selection action.
singleSelectionCallback: C OrderedCollection new Specifies a list of callbacks that is
called when an item is selected in
single selection mode.

CallData type is CwListCallbackData.


topItemPosition: CSG 1 Specifies the position of the item that
is the first visible item in the list.
visibleItemCount: CSG 1 Specifies the number of items that can
fit in the visible space of the List
work area.
CwList also inherits resource and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

476 IBM Smalltalk: Programmers Reference


Table 74. CwMainWindow resource and callback methods
menuBar: CSG 1 Specifies the widget to be laid out as the
MenuBar.
CwMainWindow also inherits resource and callback methods from CwScrolledWindow, CwComposite, CwBasicWidget,
and CwWidget. Consult the resource tables of these classes for more information.

Table 75. CwMessageBox resource and callback methods


cancelCallback: C OrderedCollection new Specifies the list of callbacks that is
called when the user clicks on the
cancel button.

CallData type is
CwAnyCallbackData.
defaultButtonType: CSG XmDIALOGOKBUTTON Specifies the default PushButton.
dialogType: CSG XmDIALOGMESSAGE Specifies the type of MessageBox
dialog, which determines the
default message symbol.
messageAlignment: CSG XmALIGNMENTBEGINNING Controls the alignment of the
message Label.
messageString: CSG nil Specifies the string to be used as
the message.
symbolIcon: CSG dynamic Specifies the icon to be used as the
message symbol.
symbolPixmap: CSG dynamic Specifies the pixmap label to be
used as the message symbol.
CwMessageBox also inherits resource and callback methods from CwCompositeBox, CwBulletinBoard, CwComposite,
CwBasicWidget, and CwWidget. Consult the resource tables of these classes for more information.

Table 76. CwOverrideShell resource and callback methods


CwOverrideShell inherits resource and callback methods from CwShell, CwBasicWidget, and CwWidget . Consult the
resource tables of these classes for more information.

Table 77. CwPrimitive resource and callback methods


backgroundColor: CSG dynamic Specifies the background drawing color.
foregroundColor: CSG dynamic Specifies the foreground drawing color.
helpCallback: C OrderCollection new Specifies the list of callbacks that is called
when the help key sequence is pressed.

CallData type is CwAnyCallbackData.


highlightThickness: CSG dynamic Specifies the size of the highlighting
rectangle.
navigationType: CSG XmNONE Controls whether the Widget is a navigation
group.
shadowThickness: CSG dynamic Specifies the size of the drawn border
shadow.
traversalOn: CSG true Specifies if traversal is activated for this
widget.
CwPrimitive also inherits resource and callback methods from CwBasicWidget and CwWidget. Consult the resource
tables of these classes for more information.

Appendix A. Widget resources and callbacks 477


Table 78. CwPushButton resource and callback methods
activateCallback: C OrderedCollection new Specifies the list of callbacks that is called
when PushButton is activated.

CallData type is CwAnyCallbackData.


armCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is armed.

CallData type is CwAnyCallbackData.


disarmCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is disarmed.

CallData type is CwAnyCallbackData.


marginBottom: CSG dynamic Overrides default value from CwLabel.
marginLeft: CSG dynamic Overrides default value from CwLabel.
marginRight: CSG dynamic Overrides default value from CwLabel.
marginTop: CSG dynamic Overrides default value from CwLabel.
showAsDefault: CSG 0 Specifies a shadow thickness for a second
shadow to be drawn around the PushButton
to visualy mark it as a default button.
traversalOn: CSG true Overrides default value from CwLabel.
CwPushButton also inherits resource and callback methods from CwLabel, CwPrimitive, CwBasicWidget, and CwWidget.
Consult the resource tables of these classes for more information.

Table 79. CwRowColumn resource and callback methods


adjustLast: CSG true Extends the last row of children to
the bottom edge of RowColumn
(when XmOrientation is
XmHORIZONTAL) or extends the
last column to the right edge of
RowColumn (when XmOrientation is
XmVERTICAL).
buttonMnemonics: C nil Used with the simple menu creation
routines.
buttons: C nil Used with the simple menu creation
routines.
buttonSet: C -1 Used with the simple menu creation
routines.
buttonType: C nil Used with the simple menu creation
routines.
entryAlignment: CSG XmALIGNMENTBEGINNING Specifies the alignment type for
CwLabel children when
XmNisAligned is enabled.
entryBorder: CSG 0 Imposes a uniform border width
upon all RowColumns children.
entryCallback: C OrderedCollection new Disables the activation callbacks for
all ToggleButton, PushButton, and
CascadeButton widgets contained
within the RowColumn widget.

478 IBM Smalltalk: Programmers Reference


Table 79. CwRowColumn resource and callback methods (continued)
entryClass: CSG dynamic Specifies the only widget class that
can be added to the RowColumn
widget; this resource is meaningful
only when the XmNisHomogeneous
resource is set to true.
isAligned: CSG true Specifies text alignment for each item
within the RowColumn widget; this
applies only to items that are a
subclass of CwLabel, and on some
platforms, applies only to instances of
CwLabel.
isHomogeneous: CSG dynamic Indicates if the RowColumn widget
should enforce exact homogeneity
among the items it contains; if true,
only the widgets that are of the class
indicated by XmNentryClass are
allowed as children of the
RowColumn widget.
labelString: C nil A text string that displays the label to
the left of the selection area when
XmNrowColumnType is set to
XmMENUOPTION.
mapCallback: C OrderedCollection new Specifies a widget-specific callback
function that is invoked when the
window associated with the
RowColumn widget is about to be
mapped.

CallData type is CwAnyCallbackData.


marginHeight: CSG dynamic Specifies the amount of space
between the top edge of the
RowColumn widget and the first item
in each column, and the bottom edge
of the RowColumn widget and the
last item in each column.
marginWidth: CSG 3 Specifies the amount of space
between the left edge of the
RowColumn widget and the first item
in each row, and the right edge of the
RowColumn widget and the last item
in each row.
menuHelpWidget: CSG nil Specifies the widget for the
CascadeButton, which is treated as
the Help widget if
XmNrowColumnType is set to
XmMENUBAR.
menuHistory: CSG nil Specifies the widget of the last menu
entry to be activated.
mnemonic: CSG nil Specifies a keyboard shortcut for the
menu item. This resource is used
when XmNrowColumnType is set to
XmMENUOPTION.

Appendix A. Widget resources and callbacks 479


Table 79. CwRowColumn resource and callback methods (continued)
numColumns: CSG 1 Specifies the number of minor
dimension extensions that are made
to accommodate the entries; this
attribute is only meaningful when
XmNpacking is set to
XmPACKCOLUMN.
optionLabel: C nil Used with the simple menu creation
routines.
orientation: CSG dynamic Determines whether RowColumn
layouts are row major or column
major.
packing: CSG dynamic Specifies how to pack the items
contained within a RowColumn
widget.
postFromButton: C nil Used with the simple menu creation
routines.
radioAlwaysOne: CSG true When set to true, forces the active
ToggleButton to be automatically
selected after having been unselected
(if no other toggle was activated).
radioBehavior: CSG false Specifies a Boolean value that when
true, indicates that the RowColumn
widget should enforce a
RadioBox-type behavior on all of its
children that are ToggleButtons.
resizeHeight: CSG true When set to true, requests a new
height if necessary.
resizeWidth: CSG true When set to true, requests a new
width if necessary.
rowColumnType: CSG XmWORKAREA Specifies the type of RowColumn
widget which is to be created.
simpleCallback: C OrderedCollection new Used with the simple menu creation
routines.

CallData type is integer.


spacing: CSG dynamic Specifies the horizontal and vertical
spacing between items contained
within the RowColumn widget.
subMenuId: CSG nil Specifies the widget for the Pulldown
MenuPane to be associated with an
OptionMenu. The cascade button and
its associated pulldown menu must
have the same (or common) parent.
unmapCallback: C OrderedCollection new Specifies a list of callbacks that is
called after the window associated
with the RowColumn widget has
been unmapped.

CallData type is CwAnyCallbackData.


CwRowColumn also inherits resource and callback methods from CwComposite, CwBasicWidget, and CwWidget.
Consult the resource tables of these classes for more information.

480 IBM Smalltalk: Programmers Reference


Table 80. CwScale resource and callback methods
decimalPoints: CSG 0 Specifies the number of decimal points to
shift the slider value when displaying it.
dragCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the slider position changes as the slider
is being dragged.

CallData type is CwValueCallbackData.


fontList: CSG dynamic Specifies the font list to use for the title text
string specified by XmNtitleString.
maximum: CSG 100 Specifies the sliders maximum value. Set
maximum before minimum to avoid warning
on x.
minimum: CSG 0 Specifies the sliders minimum value. Set
maximum before minimum to avoid warning
on x.
navigationType: CSG XmNONE Overrides default value from CwComposite.
orientation: CSG XmVERTICAL Displays Scale vertically or horizontally.
processingDirection: CSG XmMAXONTOP Specifies whether the value for
XmNmaximum is on the right or left side of
XmNminimum for horizontal Scales or above
or below XmNminimum for vertical Scales.
showValue: CSG false Specifies if a label for the current slider value
should be displayed next to the slider.
titleString: CSG nil Specifies the title text string to appear in the
scale widget window.
traversalOn: CSG true When set to true, specifies the Scales slider is
to have traversal on.
value: CSG 0 Specifies the sliders current position along
the scale, between minimum and maximum.
valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the value of the slider has changed.

CallData type is CwValueCallbackData.


CwScale also inherits resource and callback methods from CwComposite, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

Table 81. CwScrollBar resource and callback methods


decrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an arrow is selected decreasing the
slider value by one increment.

CallData type is CwValueCallbackData.


dragCallback: C OrderedCollection new Specifies the list of callbacks that is called on
each incremental change of position when the
slider is being dragged.

CallData type is CwValueCallbackData.


increment: CSG 1 Specifies the amount to move the slider when
the corresponding arrow is selected.

Appendix A. Widget resources and callbacks 481


Table 81. CwScrollBar resource and callback methods (continued)
incrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an arrow is selected increasing the
slider value by one increment.

CallData type is CwValueCallbackData.


maximum: CSG 0 Specifies the sliders maximum value. Set
maximum before minimum to avoid warning
on x.
minimum: CSG 0 Specifies the sliders minimum value. Set
maximum before minimum to avoid warning
on x.
navigationType: CSG XmTABGROUP Overrides default value from CwPrimitive.
orientation: CSG XmVERTICAL Specifies whether the ScrollBar is displayed
vertically or horizontally.
pageDecrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the slider area is selected and the slider
value is decreased by one page increment.

CallData type is CwValueCallbackData.


pageIncrement: CSG 10 Specifies the amount to move the slider when
selection occurs on the slide area.
pageIncrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the slider area is selected and the slider
value is increased by one page increment.

CallData type is CwValueCallbackData.


processingDirection: CSG XmMAXONBOTTOM Specifies whether the value for
XmNmaximum should be on the right or left
side of XmNminimum for horizontal
ScrollBars or above or below XmNminimum
for vertical ScrollBars.
sliderSize: CSG 0 Specifies the size of the slider between the
values of 0 and maximum - minimum.
toBottomCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user moves the slider to the bottom
of the scroll bar.

CallData type is CwValueCallbackData.


toTopCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user moves the slider to the top of
the scroll bar.

CallData type is CwValueCallbackData.


value: CSG 0 Specifies the sliders position between
minimum and maximum.

482 IBM Smalltalk: Programmers Reference


Table 81. CwScrollBar resource and callback methods (continued)
valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the slider is released while being
dragged; this is in place of
XmNincrementCallback,
XmNdecrementCallback,
XmNpageIncrementCallback, or
XmNpageDecrementCallback when they do
not have any callbacks attached.

CallData type is CwValueCallbackData.


CwScrollBar also inherits resource and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult
the resource tables of these classes for more information.

Table 82. CwScrolledWindow resource and callback methods


clipBackgroundColor: CSG dynamic Specifies the background color for the
clip area.
clipBackgroundPixmap: CSG nil Specifies a pixmap for tiling the
background of the clip area.
horizontalScrollBar: CSG nil Specifies the widget that is the
horizontal ScrollBar.
scrollBarDisplayPolicy: CSG XmSTATIC Controls the automatic placement of
the ScrollBars.
scrollingPolicy: CSG XmAPPLICATIONDEFINED Performs automatic scrolling of the
work area with no application
interaction.
verticalScrollBar: CSG nil Specifies the widget ID of the vertical
ScrollBar.
visualPolicy: CS XmVARIABLE Grows the ScrolledWindow to match
the size of the work area, or it can be
used as a static viewport onto a
larger data space.
workWindow: CSG nil Specifies the widget ID of the
viewing area.
CwScrolledWindow also inherits resource and callback methods from CwComposite, CwBasicWidget, and CwWidget.
Consult the resource tables of these classes for more information.

Table 83. CwSelectionBox resource and callback methods


applyCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user clicks on the Apply button.

CallData type is CwAnyCallbackData.


applyLabelString: CSG Apply Specifies the string label for the Apply
button.
cancelCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user clicks on the Cancel button.

CallData type is CwAnyCallbackData.


dialogType: CG dynamic Determines the set of SelectionBox children
widgets that are created and managed at
initialization.
listItemCount G 0 Specifies the items in the SelectionBox list.

Appendix A. Widget resources and callbacks 483


Table 83. CwSelectionBox resource and callback methods (continued)
listItems: CSG nil Specifies the items in the SelectionBox list.
listLabelString: CSG nil Specifies the string label to appear above the
SelectionBox list containing the selection
items.
listVisibleItemCount: CSG 8 Specifies the number of items displayed in
the SelectionBox list.
mustMatch: CSG false Specifies whether the selection widget should
check if the users selection in the text edit
field has an exact match in the SelectionBox
list when the OK button is activated.
noMatchCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the user makes a selection from the text
edit field that does not have an exact match
with any of the items in the list box.

CallData type is CwAnyCallbackData.


selectionLabelString: CSG Selection Specifies the string label for the selection text
edit field.
textColumns: CSG 20 Specifies the initial width of the text window
measured in character spaces.
textString: CSG nil Specifies the text in the text edit selection
field.
CwSelectionBox also inherits resource and callback methods from CwCompositeBox, CwBulletinBoard, CwComposite,
CwBasicWidget, and CwWidget. Consult the resource tables of these classes for more information.

Table 84. CwSeparator resource and callback methods


margin: CSG 0 Specifies the space on the left and right sides
between the border of the Separator and the
line drawn for horizontal orientation.
orientation: CSG XmHORIZONTAL Displays Separator vertically or horizontally.
separatorType: CSG XmSHADOWETCHEDIN Specifies the type of line drawing to be done
in the Separator widget.
traversalOn: CSG false Overrides default value from CwPrimitive.
CwSeparator also inherits resource and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult
the resource tables of these classes for more information.

Table 85. CwShell resource and callback methods


allowShellResize: CG false Specifies that if this resource is false, then the
Shell widget instance will return
XtGeometryNo to all geometry requests from
its children.
borderWidth: CSG 1 Overrides the default value from CwWidget.
popdownCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the widget instance is popped down by
popdown.

CallData type is CwAnyCallbackData.

484 IBM Smalltalk: Programmers Reference


Table 85. CwShell resource and callback methods (continued)
popupCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the widget instance is popped up by popup.

CallData type is CwAnyCallbackData.


strictClipping: CG CwWidget strictClipping Some platforms show a dramatic speed
improvement in the time to draw windows
containing many widgets if they are not
required to ensure certain clipping
constraints.
CwShell also inherits resource and callback methods from CwBasicWidget and CwWidget. Consult the resource tables
of these classes for more information.

Table 86. CwText resource and callback methods


activateCallback: C OrderedCollection new Specifies the list of callbacks that is
called when the user presses the
default action key.

CallData type is CwAnyCallbackData.


alignment: CG XmALIGNMENTBEGINNING Specifies the text alignment when the
text widget has an editMode of
XmSINGLENEEDIT.
borderWidth: CSG 1 Overrides default value from CwWidget
columns: CSG 20 Specifies the initial width of the text
window measured in character spaces.
cursorPosition: CSG 0 Indicates the position in the text where
the current insert cursor is to be
located.
doubleClickSelect: CSG true Indicates that the widget should not
perform double-click selection
processing. Ignored on platforms
where the feature is not configurable.
editable: CSG true Indicates whether the user can edit the
text string.
editMode: CG XmSINGLELINEEDIT Specifies whether the widget supports
single line or multi line editing of text.
focusCallback: C OrderedCollection new Specifies the list of callbacks that is
called before Text has accepted input
focus.

CallData type is CwAnyCallbackData.


fontList: CSG dynamic Specifies the font list to be used for
Text.
losingFocusCallback: C OrderedCollection new Specifies the list of callbacks that is
called before Text loses input focus.

CallData type is CwAnyCallbackData.


marginHeight: CSG dynamic Specifies the distance between the top
edge of the window and the text, and
between the bottom edge of the
window and the text.

Appendix A. Widget resources and callbacks 485


Table 86. CwText resource and callback methods (continued)
marginWidth: CSG dynamic Specifies the distance between the left
edge of the window and the text, and
between the right edge of the window
and the text.
maxLength: CSG dynamic Specifies the maximum length of the
text string that can be entered into text
from the keyboard.
modifyVerifyCallback: C OrderedCollection new Specifies the list of callbacks that is
called before text is deleted from or
inserted into Text.

CallData type is
CWTextVerifyCallbackData.
navigationType: CSG XmTABGROUP Overrides default value from CsPrimitive.
rows: CSG 1 Specifies the initial height of the text
window measured in character
heights.
scrollHorizontal: CG true Adds a ScrollBar that enables the user
to scroll horizontally through text
when set to true.
scrollVertical: CSG true Adds a ScrollBar that enables the user
to scroll vertically through text when
set to true.
tabSpacing: CSG 8 Indicates the tab stop spacing.
topCharacter: CSG 0 Displays the position of text at the top
of the window.
value: CSG Specifies the displayed text String.
valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is
called after text is deleted from or
inserted into Text.

CallData type is CwAnyCallbackData.


verifyBell: CSG true Specifies whether the bell should
sound when the verification returns
without continuing the action.
wordWrap: CSG false Indicates that lines are to be broken at
word breaks.
CwText also inherits resource and callback methods from CwPrimitive, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

Table 87. CwToggleButton resource and callback methods


armCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is armed.

CallData type is CwAnyCallbackData.


disarmCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the receiver is disarmed.
indicatorOn: CSG true Specifies that a toggle indicator is drawn to
the left of the toggle text or pixmap when set
to true.

CallData type is CwAnyCallbackData.

486 IBM Smalltalk: Programmers Reference


Table 87. CwToggleButton resource and callback methods (continued)
indicatorType: CSG XmNOFMANY Specifies if the indicator is a 1-of or N-of
indicator.
marginBottom: CSG dynamic Overrides default value from CwLabel.
marginLeft: CSG dynamic Overrides default value from CwLabel.
marginRight: CSG dynamic Overrides default value from CwLabel.
marginTop: CSG dynamic Overrides default value from CwLabel.
set: CSG false Displays the button in its selected state if set
to true.
traversalOn: CSG true Overrides default value from CwLabel.
valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the ToggleButton value is changed.

CallData type is
CwToggleButtonCallbackData.
CwToggleButton also inherits resource and callback methods from CwLabel, CwPrimitive, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

Table 88. CwTopLevelShell resource and callback methods


iconic: CSG false When set to true, specifies that the
application will attempt to start as an icon
once it is realized.
screen: CSG dynamic Specifies the screen on which the widget
instance resides.
CwTopLevelShell also inherits resource and callback methods from CwWMShell, CwShell, CwBasicWidget, and
CwWidget. Consult the resource tables of these classes for more information.

Table 89. CwTransientShell resource and callback methods


CwTransientShell inherits resource and callback methods from CwWMShell, CwShell, CwBasicWidget, and CwWidget.
Consult the resource tables of these classes for more information.

Table 90. CwWidget resource and callback methods


ancestorSensitive G dynamic Specifies whether the immediate parent of the
widget will react to input events.
borderWidth: CSG 1 Specifies the width of the border that
surrounds the widgets window on all four
sides.
bottomAttachment: CSG XmATTACHNONE Specifies attachment of the bottom side of the
child. This resource is only valid if the
widgets parent is a CwForm.
bottomOffset: CSG 0 Specifies the constant offset between the
bottom side of the child and the object to
which it is attached. This resource is only
valid if the widgets parent is a CwForm.
bottomPosition: CSG 0 Determines the relative position of the
bottom side of the child. This resource is only
valid if the widgets parent is a CwForm.
bottomWidget: CSG nil Specifies the widget to which the bottom side
of the child is attached. This resource is only
valid if the widgets parent is a CwForm.

Appendix A. Widget resources and callbacks 487


Table 90. CwWidget resource and callback methods (continued)
depth G CgWindow default depth Specifies the number of bits that can be used
for each pixel in the widgets window.
destroyCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the widget is destroyed.

CallData type is CwAnyCallbackData.


dragDetectCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the user performs a mouse movement that is
the cue for the application to initiate drag
and drop processing.
height: CSG dynamic Specifies the height of the widgets window
in pixels, not including the border area.
leftAttachment: CSG XmATTACHNONE Specifies attachment of the left side of the
child. This resource is only valid if the
widgets parent is a CwForm.
leftOffset: CSG 0 Specifies the constant offset between the left
side of the child and the object to which it is
attached. This resource is only valid if the
widgets parent is a CwForm.
leftPosition: CSG 0 Determines the relative position of the left
side of the child. This resource is only valid if
the widgets parent is a CwForm.
leftWidget: CSG nil Specifies the widget to which the left side of
the child is attached. This resource is only
valid if the widgets parent is a CwForm.
mappedWhenManaged: CSG true Maps the widget (makes visible) as soon as it
is both realized and managed, if set to true.
resizable: CSG true This Boolean resource specifies whether or
not a childs request for a new size is
(conditionally) granted by the Form.
resizeCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the widget is resized. This resource is
only valid if the widgets parent is a
CwForm.

CallData type is CwAnyCallbackData.


rightAttachment: CSG XmATTACHNONE Specifies attachment of the right side of the
child. This resource is only valid if the
widgets parent is a CwForm.
rightOffset: CSG 0 Specifies the constant offset between the right
side of the child and the object to which it is
attached. This resource is only valid if the
widgets parent is a CwForm.
rightPosition: CSG 0 Determines the relative position of the right
side of the child. This resource is only valid if
the widgets parent is a CwForm.
rightWidget: CSG nil Specifies the widget to which the right side
of the child is attached. This resource is only
valid if the widgets parent is a CwForm.
sensitive: CSG true Determines whether a widget will react to
input events.

488 IBM Smalltalk: Programmers Reference


Table 90. CwWidget resource and callback methods (continued)
topAttachment: CSG XmATTACHNONE Specifies attachment of the top side of the
child. This resource is only valid if the
widgets parent is a CwForm.
topOffset: CSG 0 Specifies the constant offset between the top
side of the child and the object to which it is
attached. This resource is only valid if the
widgets parent is a CwForm.
topPosition: CSG 0 Determines the relative position of the top
side of the child. This resource is only valid if
the widgets parent is a CwForm.
topWidget: CSG nil Specifies the widget to which the top side of
the child is attached. This resource is only
valid if the widgets parent is a CwForm.
userData: CSG nil Allows the user-defined application to attach
any necessary specific data to the widget.
width: CSG dynamic Specifies the width of the widgets window
in pixels, not including the border area.
x: CSG 0 Specifies the x-coordinate of the widgets
upper left-hand corner (excluding the border)
in relation to its parent widget. Note that the
methods x: and y: in CwBasicWidget on X
Motif will not cause the widget to be moved.
This is in accordance with the Motif
specification. In order to set the x,y resources
and move the widget, you should use
method moveWidget:y:.
y: CSG 0 Specifies the y-coordinate of the widgets
upper left-hand corner (excluding the border)
in relation to its parent widget. Note that the
methods x: and y: in CwBasicWidget on X
Motif will not cause the widget to be moved.
This is in accordance with the Motif
specification. In order to set the x,y resources
and move the widget, you should use
method moveWidget:y:.
CwWidget is a subclass of Object.

Table 91. CwWMShell resource and callback methods


autoRaise: CG true Specifies effects on keyboard focus and
stacking order when the receiver or any of its
descendents are selected with the mouse.
dbcsInput: CG false Specifies that the platform-dependent DBCS
shell style will be enabled.
focusCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the shell gains or loses keyboard focus.

CallData type is CwAnyCallbackData.


icon: CSG XmUNSPECIFIEDICON Specifies an icon that could be used by the
window manager as the applications icon.
iconifyCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the shell is iconified or deiconified.

CallData type is CwAnyCallbackData.

Appendix A. Widget resources and callbacks 489


Table 91. CwWMShell resource and callback methods (continued)
iconMask: CSG XmUNSPECIFIEDPIXMAP Specifies a bitmap that could be used by the
window manager as the applications icon
mask.
iconPixmap: CSG XmUNSPECIFIEDPIXMAP Specifies a bitmap that could be used by the
window manager as the applications icon.
mwmDecorations: CG -1 Includes the decoration flags describing the
specific decorations to add or remove from
the window manager frame.
title: CSG dynamic Specifies the application name to be
displayed by the window manager. If title is
not explicitly set, the widgets name is used
by default.
windowCloseCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the shell is about to be closed by the
window manager.

CallData type is
CwConfirmationCallbackData.
CwWMShell also inherits resource and callback methods from CwShell, CwBasicWidget, and CwWidget. Consult the
resource tables of these classes for more information.

490 IBM Smalltalk: Programmers Reference


Appendix B. Extended widgets resources and callbacks
This appendix provides a detailed listing of the resources and callbacks for each
class in the Extended Widgets subsystem. Note that only the set resource methods
are shown in each table, unless a resource only has a get method. Get methods
have the same name without the colon. Create/Set/Get (CSG) access designation
and default value are listed for each resource and callback.

Callback list set methods are shown, even though callbacks are usually added
using addCallback:receiver:selector:clientData:, and not using a callback list set
method. These methods end in Callback:.

The example Table 1. CwArrowButton resource and callback methods in Appendix A.


Widget resources and callbacks on page 469 shows how to use the tables in this
appendix.
Table 92. CwExtendedComposite resource and callback methods
backgroundColor: CSG dynamic A CgRGBColor that specifies the background
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource depend on
platform-specific styles and capabilities and
vary from platform to platform.
foregroundColor: CSG dynamic A CgRGBColor that specifies the foreground
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource depend on
platform-specific styles and capabilities and
vary from platform to platform.
navigationType: CSG XmNONE Controls whether the widget is a navigation
group. Allowed values are as follows:
XmNONE Indicates that the widget is not a
navigationgroup. XmTABGROUP Indicates
that the widget is included automatically in
keyboard navigation.
traversalOn: CSG true Specifies if traversal is activated for this
widget. Note: On some platforms, a widget
that cannot be reached through keyboard
traversal will not take focus under any
conditions, for example, even when it is
clicked on with the mouse.
CwExtendedComposite also inherits resource and callback methods from CwExtendedPrimitive, CwExtendedWidget, and
CwWidget.

Table 93. CwExtendedPrimitive resource and callback methods


backgroundColor: CSG dynamic A CgRGBColor that specifies the background
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource are dependent on
platform-specific styles and capabilities and
vary from platform to platform.

Copyright IBM Corp. 1994, 2000 491


Table 93. CwExtendedPrimitive resource and callback methods (continued)
foregroundColor: CSG dynamic A CgRGBColor that specifies the foreground
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource are dependent on
platform-specific styles and capabilities and
vary from platform to platform.
navigationType: CSG XmNONE Controls whether the widget is a navigation
group. Allowed values are as follows:
XmNONE Indicates that the widget is not a
navigationgroup. XmTABGROUP Indicates
that the widget is included automatically in
keyboard navigation.
traversalOn: CSG true Specifies if traversal is activated for this
widget. Note: On some platforms, a widget
that is not reachable through keyboard
traversal will not take focus under any
conditions for example, even when it is
clicked on with the mouse.
CwExtendedPrimitive also inherits resource and callback methods from CwExtendedComposite, CwExtendedWidget, and
CwWidget.

Table 94. EwContainerList resource and callback methods


applicationDrawnBack- CSG false Specifies whether the application wants the
ground: drawBackgroundCallback to be triggered
when an items background needs to be
drawn. If this value is false, then the widget
will draw the normal default background
for each selected and nonselected item.
drawBackground- Callback: C OrderedCollection new Specifies the list of callbacks that is called
when an items background needs to be
drawn. The callback reason is
XmCRDRAWBACKGROUND. The
applicationDrawnBackground resource must
be true for this callback to be activated.
CallData type is
EwIconListDrawBackgroundCallbackData.
fontList: CSG CwFontList default Specifies the font list associated with the list
items.
itemHeight: CSG 34 Specifies the height list items in pixels. This
includes the margin height on the top and
bottom of the item and two pixels for
emphasis. If the resource is nil, each item is
individually sized to its preferred height
plus twice the margin height plus two pixels
for emphasis.
items: CSG OrderedCollection new A collection that represents hierarchy root
items in the list.

492 IBM Smalltalk: Programmers Reference


Table 94. EwContainerList resource and callback methods (continued)
visualInfoCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an items icon, label and isInUse
indicator are needed. The callback reason is
XmCRVISUALINFO. The application must
hook this callback and set the calldata icon
to the CgIcon (or other renderable object) to
be displayed as the icon for the item in
calldata item. It must also set the calldata
label to the String (or other renderable
object) to be displayed as the label for the
item. No other resources might be set while
inside this callback nor might any
nonresource API be invoked. Calldata type
is EwIconVisualInfoCallbackData.
EwContainerList also inherits resource and callback methods from EwContainerList, EwLinearList, EwList, EwScrollable,
CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 95. EwDrawnList resource and callback methods


applicationDrawnStates: CSG XmDRAWNONE Describes that visual states will be custom
drawn by the application in the
displayCallback. Any visual states not
specified in this resource are drawn by the
EwDrawnList. The integer represents the
bitwise inclusive OR of one or more of the
following values: XmDRAWNONE,
XmDRAWSELECTION,
XmDRAWCURSORED
displayCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is to be drawn. The callback
reason is XmCRLISTITEMDISPLAY. If this
callback is not hooked, the list will appear
empty when displayed. Calldata type is
EwDrawnListDrawCallbackData.
itemHeight: CSG 16 Specifies the height in pixels of items in the
list. Note: This resource is ignored if the
measureCallback is hooked.
items: CSG OrderedCollection new A collection that represents the items in the
list. Usually the item list is copied form the
application via asOrderedCollection.
However, the use of an Interval for the item
list is optimized so that a copy and
conversion is not made until an edit
operation is requested.
itemWidth: CSG 500 Specifies the width in pixels of items in the
list. Note: This resource is only used to set
the scrolling interval for the horizontal scroll
bar. It is ignored if the scrollHorizontal
resource is false.

Appendix B. Extended widgets resources and callbacks 493


Table 95. EwDrawnList resource and callback methods (continued)
measureCallback: C OrderedCollection new Specifies the list of callbacks that is called in
order to obtain the height of an item. The
application specifies the height by setting the
height field of the calldata to the Integer pixel
height of the item. If this callback is not
hooked, the itemHeight resource is used as
the height of all items. No other resources
might be set while inside this callback nor
might any nonresource API be invoked. The
callback reason is
XmCRLISTITEMMEASURE. Calldata type is
EwDrawnListDrawCallbackData.
EwDrawnList also inherits resource and callback methods from EwDrawnList, EwLinearList, EwList, EwScrollable,
CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 96. EwFlowedIconList resource and callback methods


itemHeight: CSG 34 Specifies the height of each list item in pixels.
This includes the margin height on the top
and bottom of the item and two pixels for
emphasis. Unlike other extended list widgets,
the value of this resource might not be nil.
That is, all items must have the same height.
This height is used in laying out the items
into vertical columns.
itemWidth: CSG 80 Specifies the width of each list item in pixels.
This includes the two pixels for emphasis.
Unlike other extended list widgets, the value
of this resource might not be nil. That is, all
items must have the same width. This width
is used in laying out the items into horizontal
rows.
EwFlowedIconList also inherits resource and callback methods from EwFlowedIconList, EwIconList, EwContainerList,
EwLinearList, EwList, EwScrollable, CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 97. EwIconArea resource and callback methods


applicationDrawnBack- CSG false Specifies whether the application wants the
ground: drawBackgroundCallback to be triggered
when an items background needs to be
drawn. If this value is false, then the widget
will draw the default background for each
selected and nonselected item.
beginEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is about to be edited. The
callback reason is XmCRBEGINEDIT. For
editing to occur, the application must set
calldata doit flag to true. It might also
replace the editPolicy in the calldata with a
custom edit policy appropriate for the item
being edited. Calldata type is
EwBeginEditCallbackData.

494 IBM Smalltalk: Programmers Reference


Table 97. EwIconArea resource and callback methods (continued)
drawBackground- Callback: C OrderedCollection new Specifies the list of callbacks that is called
when an items background needs to be
drawn. The callback reason is
XmCRDRAWBACKGROUND. The
applicationDrawnBackground resource must
be true for this callback to be activated.
Calldata type is EwIcon-
ListdrawBackgroundCallbackData.
endEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is done being edited. The
callback reason is XmCRENDEDIT. This
callback is activated whenever editing ends
on an item or whenever the value changes
during an edit. The application must hook
this callback and use the calldata
.brnewValue to update the value of the
application objects. Calldata type is
EwEndEditCallbackData.
fontList: CSG CwFontList default Specifies the font list associated with the list
items.
innerMargin: CSG 2 Specifies the margin width to be used
between each items icon and its label.
items: CSG OrderedCollection new A collection that represents the itemsin the
list.
labelOrientation: CSG XmRIGHT Specifies the orientation of each items label
with respect to its icon. Allowed values are
as follows: XmRIGHT The label is to the
right of the icon. XmBOTTOM The label is
below each icon.
selectedItems: CSG OrderedCollection new A collection that represents currently selected
items, either by the user or the application.
selectionPolicy: CSG XmBROWSESELECT Defines the interpretation of the selection
action. Allowed values are as follows:
XmSINGLESELECT Allows only single
selections. XmMULTIPLESELECT Allows
multiple selections. XmEXTENDEDSELECT
Allows extended selections.
XmBROWSESELECT Allows drag and
browse functionality.
XmREADONLYSELECT Allows navigation,
but no selection or callbacks.
visualInfoCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an items icon, label and isInUse
indicator are needed. The callback reason is
XmCRVISUALINFO. The application must
hook this callback and set the calldata icon
to the CgIcon (or other renderable object) to
be displayed as the icon for the item in
calldata item. It must also set the calldata
label to the String (or other renderable
object) to be displayed as the label for the
item. No other resources might be set while
inside this callback nor might any
nonresource API be invoked. Calldata type is
EwIconVisualInfoCallbackData.

Appendix B. Extended widgets resources and callbacks 495


Table 97. EwIconArea resource and callback methods (continued)
EwIconArea also inherits resource and callback methods from EwIconArea, EwList, EwScrollable, CwExtendedPrimitive,
CwExtendedWidget, and CwWidget.

Table 98. EwIconList resource and callback methods


beginEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is about to be edited. The
callback reason is XmCRBEGINEDIT. For
editing to occur, the application must set
calldata doit: to true. It might also replace the
editPolicy in the calldata with a custom edit
policy appropriate for the item being edited.
Calldata type is EwBeginEditCallbackData.
emphasisPolicy: CSG XmSEPARATE Specifies whether to draw the icon and label
emphasis as a single rectangle
(XmTOGETHER) or as two separate
rectangles (XmSEPARATE).
endEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is done being edited. The
callback reason is XmCRENDEDIT. This
callback is activated whenever editing ends
on an item or whenever the value changes
during an edit. The application must hook
this callback and use the calldata newValue
to update the value of the application objects.
Calldata type is EwEndEditCallbackData.
innerMargin: CSG 2 Specifies the margin width to be used
between each items icon and its label.
itemWidth: CSG 1 Specifies the width of list items the in pixels.
The value of this resource will be increased if
any items are encountered in the list with a
width exceeding the resource value. This
resource is used to set the scrolling interval
for the horizontal scroll bar. It is ignored if
the scrollHorizontal resource is false.
labelOrientation: CSG XmRIGHT Specifies the orientation of each items label
with respect to its icon. Allowed values are as
follows: XmRIGHT The label is to the right of
the icon. XmBOTTOM The label is below
each icon.
visualInfoCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an items icon, label and isInUse
indicator are needed. The callback reason is
XmCRVISUALINFO. The application must
hook this callback and set the calldata icon to
the CgIcon (or other renderable object) to be
displayed as the icon for the item in calldata
item. It must also set the calldata label to the
String (or other renderable object) to be
displayed as the label for the item. No other
resources might be set while inside this
callback nor might any nonresource API be
invoked. Calldata type is
EwIconVisualInfoCallbackData.
EwIconList also inherits resource and callback methods from ExIconList, EwContainerList, EwLinearlist, EwList,
EwScrollable, CwExtendedWidget, and CwWidget.

496 IBM Smalltalk: Programmers Reference


Table 99. EwIconTree resource and callback methods
childrenCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an items list of children is
needed. The callback reason is
XmCRCHILDREN. The application must
set the calldata children to the Collection
of children objects of the item in calldata
item. Calldata type is
EwChildrenCallbackData.
expandCollapse- Callback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is expanded or
collapsed. The callback reason is
| XmCREXPAND or XmCRCOLLAPSE.
Calldata type is EwListCallbackData.
hierarchyPolicy: CSG EwHierarchyPolicy new An instance or subclass of
EwHierarchyPolicy that defines how to
display and manage the hierarchy.
itemCount G 0 Specifies the total number of items. This
number might not match the items
resource because this number is the size
of the expanded hierarchy. It is
automatically updated by the list
whenever an element is added to or
deleted from the list.
items: CSG OrderedCollection new A collection that represents hierarchy
root items in the list.
EwIconTree also inherits resource and callback methods from EwIconTree, EwIconList, EwContainerList, EwLinearList,
EwList, EwScrollable, CwExtendedWidget, and CwWidget.

Table 100. EwLinearList resource and callback methods


items: CSG OrderedCollection new The collection of items to be displayed.
SelectedItemCount G 0 Specifies the number of objects in the selected
item list.
selectedItems: CSG OrderedCollection new The collection of items that are currently
selected, either by the user or the application.
topItemPosition: CSG 1 Specifies the integer position of the item that
is the first visible item in the list. Setting this
resource is equivalent to calling the setPos:
function. The position of the first item in the
list is 1; the position of the second item is 2;
and so on. A position of 0 specifies the last
item in the list.
visibleItemCount G dynamic Represents the number of items that can fit in
the visible space of the list. Until the list is
realized, the answer is 0.
EwLinearList also inherits resource and callback methods from these classes: EwLinearList, EwList, EwScrollable,
CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Appendix B. Extended widgets resources and callbacks 497


Table 101. EwList resource and callback methods
backgroundColor: CSG dynamic A CgRGBColor that specifies the
background drawing color. changing
this resource depend on
platform-specific styles and capabilities
and vary from platform to platform.
browseSelectionCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is selected in the
browse selection mode. The callback
reason is XmCRBROWSESELECT.
Calldata type is
EwListSelectionCallbackData.
defaultActionCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is double clicked.
The callback reason is
XmCRDEFAULTACTION. Calldata type
is EwListSelectionCallbackData.
extendedSelectionCallback: C OrderedCollection new Specifies the list of callbacks that is
called when items are selected using the
extended selection mode. The callback
reason is XmCREXTENDEDSELECT.
Calldata type is
EwListSelectionCallbackData.
foregroundColor: CSG dynamic A CgRGBColor that specifies the
foreground drawing color. Note: The
particular aspects of the widgets
appearance that are affected by
changing this resource depend on
platform-specific styles and capabilities
and vary from platform to platform.
itemCount G 0 The total number of items. This number
matches the items resource. It is
automatically updated by the list
whenever an element is added to or
deleted from the list.
items: CSG OrderedCollection new The collection of items to be displayed.
modifyVerifyCallback: C OrderedCollection new Specifies the list of callbacks that is
called when the selection is about to be
changed. The application might undo
the selection change by setting the doit
field of the calldata to false. Calldata
type is CwConfirmationCallbackData.
multipleSelection- Callback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is selected in
multiple selection mode. The callback
reason is XmCRMULTIPLESELECT.
Calldata type is
EwListSelectionCallbackData.

498 IBM Smalltalk: Programmers Reference


Table 101. EwList resource and callback methods (continued)
scrollHorizontal: CG true Specifies whether a horizontal scroll bar
should be used for the list. This
resource will only have an effect if the
widget is created using the
createScrolledWidget:- parent:argBlock:
or createScrolledManagedWidget:-
parent:argBlock: message. The
horizontal scroll bar will always be
present, even when the window is
larger than the item width.
selectionPolicy: CSG XmBROWSESELECT Defines the interpretation of the
selection action. Valid values are:
XmSINGLESELECT Allows only single
selections. XmMULTIPLESELECT
Allows multiple selections.
XmEXTENDEDSELECT Allows
extended selections.
XmBROWSESELECT Allows drag and
browse functionality.
XmREADONLYSELECT Allows
navigation, but no selection or
callbacks.
singleSelectionCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is selected in single
selection mode. The callback reason is
XmCRSINGLESELECT. Calldata type is
EwListSelectionCallbackData.
EwList also inherits resource and callback methods from EwList, EwScrollable, CwExtendedPrimitive,
CwExtendedWidget, and CwWidget.

Table 102. EwNotebook resource and callback methods


backgroundColor: CSG dynamic A CgRGBColor that specifies the background
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource depend on
platform-specific styles and capabilities and
vary from platform to platform.
currentPage: CSG dynamic The instance of EwPage that is currently on
top.
fontList: SG CwFontList default Specifies the font list for any text widget in
the notebook.
pageChangeCallback: C OrderedCollection new Specifies the list of callbacks that is called just
before any switching of pages takes place.
This event is triggered when a new page is
being brought to the top in the receiver. The
callback reason is XmCRPAGECHANGE.
Calldata type is CwPageChangeCallbackData.
EwNotebook also inherits resource and callback methods from EwNotebook, CwExtendedComposite, CwExtendedWidget,
and CwWidget.

Appendix B. Extended widgets resources and callbacks 499


Table 103. EwPage resource and callback methods
buttonFontList: CSG nil Defines the font used for button children of
the page (that is, push buttons and toggle
buttons). This resource has no effect and is
only provided for protocol compatiability
with CwForm.
cancelButton: SG nil Specifies the widget that is the Cancel button.
CwBulletinBoard subclasses, which define a
Cancel button, set this resource.
CwBulletinBoard does not directly provide
any behavior for the button. This resource
has no effect and is only provided for
protocol compatibility with CwForm
defaultButton: SG nil Specifies the widget which is the default
button.CwBulletinBoard subclasses, which
define a default button, set this resource.
CwBulletinBoard causes this button to be
activated when the return key is pressed.
This resource has no effect and is only
provided for protocol compatibility with
CwForm.
fractionBase: CSG 100 Specifies the denominator used in calculating
the relative position of a child widget using
XmATTACHPOSITION constraints.
horizontalSpacing: CSG 0 Specifies the offset for right and left
attachments.
labelFontList: CSG nil Defines the font used for label children of the
page. A value of nil specifies that the default
font should be used. This resource has no
effect and is only provided for protocol
compatiability with CwForm.
marginHeight: CSG 0 Specifies the minimum spacing in pixels
between the top or bottom edge of the page
and any child widget.
marginWidth: CSG 0 Specifies the minimum spacing in pixels
between the left or right edge of the page
and any child widget.
pageEnterCallback: C OrderedCollection new Specifies the list of callbacks that is called just
before a page is to be managed. This happens
when the receiver is being brought to the top
in a parent notebook. The callback reason is
XmCRPAGEENTER. Calldata type
isCwAnyCallbackData.
pageLabel: CSG nil Specifies the String to place in the status
label. Note: This resource is ignored if the
page is added to an EwWINNotebook.
pageLeaveCallback: C OrderedCollection new Specifies the list of callbacks that is called just
before a page is to be unmanaged. This
happens when another page is being brought
to the top in a parent notebook. By setting
the doit field in the callback structure to false,
the change to the new page will not take
place The callback reason is
XmCRPAGELEAVE. Calldata type
isCwConfirmationCallbackData.

500 IBM Smalltalk: Programmers Reference


Table 103. EwPage resource and callback methods (continued)
resizePolicy: CSG XmRESIZEANY Controls the policy for resizing the pages
widgets. Allowed values are as follows:
XmRESIZENONE Fixed size. XmRESIZEANY
Shrink or grow as needed. XmRESIZEGROW
Grow only.
rubberPositioning: CSG false Indicates the default attachment for a child of
the page. If this resource is set to false, then
the left and top of the child is by default
attached to the left and top side of the page.
If this resource is set to true, then the child is
by default attached to its initial position in
the page.
tabBackgroundColor: CSG nil A CgRGBColor that specifies the color to
draw the background of the pages tab.
tabLabel: CSG nil Specifies the renderable object to draw in the
pages tab.
tabType: CG XmMAJOR Specifies the type of tab to use for the page.
Allowed values are as follows: XmMAJOR
These are the primary pages of a notebook.
XmMINOR These are essentially children of
the major page they are added after. They
display only after their parent major page is
displayed They appear perpendicular to the
major tabs. XmNONE These are major pages
with no tabs. Note: This resource is ignored if
the page is added to a EwWINNotebook.
textFontList: CSG nil Specifies the font list used for the pages text
children. If the buttonFontList resource is not
specified, then this resource is also used for
buttons. If the labelFontList resource is not
specified, then this resource is also used for
labels. This resource has no effect and is only
provided for protocol compatibility with
CwForm.
verticalSpacing: CSG 0 Specifies the offset for top and bottom
attachments.
EwPage also inherits resource and callback methods from EwPage, CwExtendedComposite, CwExtendedWidget, and
CwWidget.

Table 104. EwPMNotebook resource and callback methods


backPagePosition: CSG XmBOTTOMRIGHT Specifies where the simulated back pages
appear. Allowed values are as follows:
XmBOTTOMRIGHT Back pages are drawn
bottom and right. XmTOPRIGHT Back pages
are drawn top and left.
bindingType: CSG XmNONE Determines the style of binding displayed
along the edge of the notebook across major
tabs. Allowed values are as follows:
XmNONE No binding is drawn. XmSOLID A
solid binding is drawn. XmSPIRAL A spiral
pixmap is drawn.

Appendix B. Extended widgets resources and callbacks 501


Table 104. EwPMNotebook resource and callback methods (continued)
foregroundColor: CSG dynamic A CgRGBColor that specifies the foreground
drawing color. Note: The particular aspects of
the widgets appearance that are affected by
changing this resource depend on
platform-specific styles and capabilities and
vary from platform to platform.
majorTabHeight: CSG 25 Specifies the height of the notebooks major
tabs in pixels.
majorTabWidth: CSG 50 Specifies the width of the notebooks major
tabs in pixels.
minorTabHeight: CSG 25 Specifies the height of the notebooks minor
tabs in pixels.
minorTabWidth: CSG 50 Specifies the width of the notebooks minor
tabs in pixels.
orientation: CSG XmHORIZONTAL Specifies on what plane the pages turn. The
major tabs being opposite the binding.
Allowed values are as follows:
XmHORIZONTAL Major tabs appear on the
right and minor tabs are either on the top or
bottom depending on the back page position.
XmVERTICAL Major tabs are either on the
top or bottom depending on the back page
position and minor tabs appear on the right.
pageButtonHeight: CSG 20 Specifies the height of the notebooks page
buttons in pixels. A value of 0 means there
are no buttons or page label visible.
pageButtonWidth: CSG 20 Specifies the width of the notebooks page
buttons in pixels. A value of 0 means there
are no buttons or page label visible.
EwPMNotebook also inherits resource and callback methods from these classes: EwPMNotebook, EwNotebook,
CwExtendedComposite, CwExtendedWidget, and CwWidget.

Table 105. EwScrollable resource and callback methods


EwScrollable does not define any additional resource or callback methods. EwScrollable inherits resource and
callback mehtods from EwScrollable, CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 106. EwSlider resource and callback methods


activeScale: CSG XmTOPORLEFT Specifies which scale is the active scale. The
active scale indicates the scale to be used
when positioning the slider arm. Allowed
values are as follows: XmTOPORLEFT Top or
left scale, depending on the value of the
orientation resource. XmBOTTOMORRIGHT
Bottom or right scale, depending on the value
of the orientation resource.
bottomOrRightScale- CSG 1 Specifies the size of the increments between
Resolution the minimum and maximum values for the
bottom or right scale. For example, a
resolution of 2 for a scale with min = 0 and
max = 100 would result in increments 0, 2, 4,
6,...,100.

502 IBM Smalltalk: Programmers Reference


Table 106. EwSlider resource and callback methods (continued)
bottomOrRightScale- CSG 1 Specifies the amount to be added to or
ShaftIncrement: subtracted form the current value when the
left mouse button is pressed inside the shaft
to the left or right of the slider arm,
respectively. The value of this resource must
be a multiple of the
bottomOrRightScaleResolution resource.If the
shaft increment is not a multiple of the
resolution, it is added to the current value,
then rounded to the nearest multiple of the
resolution, with the result being a minimum
movement of one resolution unit.
bottomOrRightScale- CSG 0 Specifies the value associated with the
Value: sliders current position along the bottom or
right scale, between the minimum and
maximum. If the value is out of range, the
current value is set to the current maximum
or minimum, whichever is closer. This is
equivalent to sending setCurrentValue:notify:
with the notify parameter set to true.
bottomOrRightScale- CSG 0 Specifies the sliders maximum value for the
Max: bottom or right scale. The value of this
resource must be greater than the
bottomOrRightScaleMin resource value.
bottomOrRightScale- CSG 0 Specifies the sliders minimum value for the
Min: bottom or right scale.The value of this
resource must be less than the value of the
bottomOrRightScaleMax resource.
buttonStyle: CSG XmBUTTONSNONE Specifies where buttons should be displayed
relative to the shaft. Allowed values are:
XmBUTTONSSPLIT One button on either end
of the shaft. XmBUTTONSBEGINNING For
vertical sliders, both buttons above shaft; for
horizontal sliders, both buttons to the left of
the shaft. XmBUTTONSEND For vertical
sliders, both buttons below shaft; for
horizontal sliders, both buttons to the right of
the shaft. XmBUTTONSNONE No buttons.
currentValue: CSG 0 Specifies the value associated with the
sliders current position along the active
scale, between the minimum and maximum.
dragCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the slider position changes as the arm
is being dragged. The reason is XmCRDRAG.
Calldata type is EwSliderValuesCallbackData
focusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before the slider has accepted input focus.
The reason is XmCRFOCUS. Calldata type is
CwAnyCallbackData.
fontList: CSG dynamic Specifies the font list used for any text
widget.
horizontalMargin: CSG 12 Specifies the number of pixels to be used as a
margin between the slider components
(scales, shaft, buttons) and the left and right
edges of the widgets area.

Appendix B. Extended widgets resources and callbacks 503


Table 106. EwSlider resource and callback methods (continued)
losingFocusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before the slider loses input focus. The reason
is XmCRLOSINGFOCUS. Calldata type is
CwAnyCallbackData.
orientation: CSG XmHORIZONTAL Specifies whether slider should be displayed
vertically or horizontally. Allowed values are
XmHORIZONTAL and XmVERTICAL.
readOnly: CSG false Specifies whether the slider is being used as a
read-only status indicator. If so, the value of
the ribbonStrip resource is set to true and no
slider arm or buttons are displayed.
ribbonStrip: CSG false Specifies whether the area between the
minimum and the slider arm should be filled.
This is automatically set to true if the valueof
the readOnly resource is set to true.
snapToResolution: CSG true Specifies the current snap policy, which
regulates the positioning of the slider arm
when the mouse button is released after
dragging. The sliders current value will
always be a multiple of the resolution,
regardless of the snap policy. The resource
value true means align the slider arm with
the nearest scale value (the nearest multiple
of the resolution). The resource value false
means do not adjust the position of the slider
arm. The value will still be a factor of the
resolution, but the arm might be positioned
between scale values.
thickness: CSG 16 Specifies the slider shaft thickness in pixels.
The size of the slider arm is automatically
adjusted. Specifying a size of less than 3
pixels will result in the value of the resource
being set to 3.
topOrLeftScaleMax: CSG 0 Specifies the sliders maximum value for the
top or left scale.The value of this resource
must be greater than the value of the
topOrLeftScaleMin resource.
topOrLeftScaleMin: CSG 0 Specifies the sliders minimum value for the
top or left scale.The value of this resource
must be less than the value of the
topOrLeftScaleMax resource.
topOrLeftScale- CSG 1 Specifies the size of the increments between
Resolution the minimum and maximum values for the
top or left scale. For example, a resolution of
2 for a scale with min = 0 and max = 100
would result in increments 0, 2, 4, 6,...,100.

504 IBM Smalltalk: Programmers Reference


Table 106. EwSlider resource and callback methods (continued)
topOrLeftScale- CSG 1 Specifies the amount to be added to or
ShaftIncrement: subtracted form the current value when the
left mouse button is pressed inside the shaft
above or below the slider arm, respectively.
The value of this resource must be a multiple
of the topOrleftScaleResolution resource.If the
shaft increment is not a multiple of the
resolution, it is added to the current value,
then rounded to the nearest multiple of the
resolution, with the result being a minimum
movement of one resolution unit.
topOrLeftScaleValue: CSG 0 Specifies the value associated with the
sliders current position along the top or left
scale, between the minimum and maximum.
If the value is out of range, the current value
is set to the current maximum or minimum,
whichever is closer. This is equivalent to
sending setCurrentValue:notify: with the
notify parameter set to true.
valueChangedCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the value of the slider has changed.
This is called as the result of a button press,
arrow key press, or releasing the slider arm
after a drag. The reason is
XmCRVALUECHANGED. Calldata type is
EwSliderValuesCallbackData.
verticalMargin: CSG 12 Specifies the number of pixels to be used as a
margin between the slider components
(scales, shaft, buttons) and the top and
bottom edges of the widgets area.
EwSlider also inherits resource and callback methods from EwSlider, CwExtendedPrimitive, CwExtendedWidget, and
CwWidget.

Table 107. EwSpinButton resource and callback methods


activateCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the default action key is pressed. The
callback reason is XmCRACTIVATE. Calldata
type is CwValueCallbackData.
backgroundColor: CSG dynamic A CgRGBColor that represents the
background color of the entry field area.
decrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the spin button value decreases by one
step. This can happen either when the down
arrow key is pressed or the down arrow
button is selected with the mouse. The
callback reason is XmCRDECREMENT.
Calldata type is
CwValueChangedCallbackData.
editable CSG true Specifies whether users can edit text in the
entry field part of the spin button.
focusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before the entry field has accepted input
focus. The callback reason is XmCRFOCUS.
Calldata type is CwAnyCallbackData.

Appendix B. Extended widgets resources and callbacks 505


Table 107. EwSpinButton resource and callback methods (continued)
fontList: CSG dynamic Specifies the font list used for any text
widget.
foregroundColor: CSG dynamic A CgRGBColor that represents the
background color of the entry field area
increment: CSG 1 Specifies the amount to increase or decrease a
numeric spin button when the corresponding
arrow is selected. Because button semantics
designate whether the increment is added to
or subtracted form the current value, all
increment values specified are absolute
values. If the itemType is XmSBSTRING this
resource is ignored.
incrementCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the spin button value increases by one
step. This can happen either when the up
arrow key is pressed or the up arrow button
is selected with the mouse. The callback
reason is XmCRINCREMENT. Calldata type
is CwValueChangedCallbackData.
items: CSG nil A collection of strings that is the list of items
in a spin button with the itemType resource
set to XmSBSTRING.
itemType: CG XmSBNUMERIC Specifies the type of items in the spin button.
Allowed values are XmSBNUMERIC and
XmSBSTRING.
losingfocusCallback: C OrderedCollection new Specifies the list of callbacks that is called
before the entry field loses input focus. The
callback reason is XmCRLOSINGFOCUS.
Calldata type is CwAnyCallbackData.
maximum: CSG 0 Specifies the spin buttons maximum value
for spin buttons whith the itemType resource
set to XmSBNUMERIC.
minimum: CSG 0 Specifies the spin buttons minimum value
for spin buttons whith the itemType resource
set to XmSBNUMERIC.
modifyVerifyCallback: C OrderedCollection new Specifies the list of callbacks that is called
before text is deleted form or inserted into
the entry field. The callback reason is
XmCRMODIFYINGTEXTVALUE. Calldata
type is CwTextVerifyCallbackData.
value: CSG 0 A String or Integer that represents the current
value of the entry field.
valueChangedCallback C OrderedCollection new Specifies the list of callbacks that is called
after text is deleted form or inserted into the
entry field. The callback reason is
XmCRVALUECHANGED. Calldata type is
CwAnyCallbackData.
wrap: CSG true Specifies whether the spin button should
cycle or stop upon reaching the end of the
collection for a spin button with an itemType
of XmSBSTRING, or maximum or minimum
value for a numeric spin button (itemType is
XmSBNUMERIC).

506 IBM Smalltalk: Programmers Reference


Table 107. EwSpinButton resource and callback methods (continued)
EwSpinButton also inherits resource and callback methods from EwSpinButton, CwExtendedPrimitive,
CwExtendedWidget, and CwWidget.

Table 108. EwTableColumn resource and callback methods


beginEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is about to be edited. The
callback reason is XmCRBEGINEDIT. For
editing to occur, the application must the set
calldata doit: flag to true. It might also
replace the editPolicy in the calldata with a
custom edit policy appropriate for the cell
being edited. Calldata type is
EwBeginEditCallbackData.
cellValueCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an items cell value is needed for this
column. The callback reason is
XmCRCELLVALUE. The application must
hook this callback and set the calldata value
to the renderable object to appear in the cell
specified in the calldata. Calldata type is
EwCellValueCallbackData.
editable: CSG true Specifies whether the cells in this column are
editable. For a cell to be editable, the table
widget must be editable, the column must be
editable, and the application must hook the
beginEditCallback and set the calldata doit
flag to true.
endEditCallback: C OrderedCollection new Specifies the list of callbacks that is called
when an item is done being edited. The
callback reason is XmCRENDEDIT. This
callback is activated whenever editing ends
on a cell or whenever the value changes
during an edit. The application must hook
this callback and use the calldata newValue
to set the changed value of the apporpriate
application objects. Calldata type is
EwEndEditCallbackData.
etched: CSG false Specifies whether this column is to be etched.
If the tables visualStyle is XmETCHCELLS,
then this resource determines whether all
cells in this column are to be drawn as
etched. Any other value for the tables
visualStyle causes this resource to be ignored.
heading: CSG nil Any renderable objects (such as a String or
CgIcon) that specifies the heading object to be
displayed at the top of the column. The
height of the heading row is the maximum of
the heights of the headings of the columns.
horizontalAlignment: CSG XmALIGNMENT- Specifies how the cells in this column should
BEGINNING be aligned horizontally. Allowed values are
XmALIGNMENTBEGINNING,
XmALIGNMENTCENTER, and
XmALIGNMENTEND.

Appendix B. Extended widgets resources and callbacks 507


Table 108. EwTableColumn resource and callback methods (continued)
horizontalHeading- CSG XmALIGNMENT- Specifies how the columns heading should be
Alignment: BEGINNING aligned horizontally. Allowed values are
XmALIGNMENTBEGINNING,
XmALIGNMENTCENTER, and
XmALIGNMENTEND.
outerWidth: SG dynamic Specifies the width of the column in pixels.
This does include the width of any emphasis
or vertical separator.
resizable: CSG false Specifies whether the column is resizable by
users. If true, users can drag the right side of
the column heading to change the column
width.
resizeCallback: C OrderedCollection new Specifies the list of callbacks that is called
when the column is resized by users. The
callback reason is XmCRRESIZE. Calldata
type is CwAnyCallbackData.
showInUse: CSG false Specifies whether in-use emphasis should be
displayed in the column for each list item.
userData: CSG nil An Object that represents the user data for
the column.
verticalAlignment: CSG XmALIGNMENT- Specifies how the cells in this column should
BEGINNING be aligned vertically. Allowed values are
XmALIGNMENTBEGINNING,
XmALIGNMENTCENTER, and
XmALIGNMENTEND.
verticalSeparator- CSG 1 Specifies the thickness of the line shown to
Thickness: the right of each cell in the column.
width: CSG 32 Specifies the width of the column in pixels.
This does not includes the width of any
emphasis or vertical separator.
EwTableColumn also inherits resource and callback methods from EwTableColumn and Object.

Table 109. EwTableList resource and callback methods


cellBlockSelection- C OrderedCollection new Specifies the list of callbacks that is called
Callback: whenever a block of cells is selected in block
selection mode. The reason is
XmCRCELLBLOCKSELECT. Calldata type is
EwTableListSelectionCallbackData.
cellSingleSelection- C OrderedCollection new Specifies the list of callbacks that is called
Callback: when an item is selected in cell single
selection mode. The reason is
XmCRCELLSINGLESELECT. Calldata type is
EwTableListSelectionCallbackData.
columnHeading- C OrderedCollection new Specifies the list of callbacks that is called
SelectionCallback: when a column heading, or set of headings,
is selected or deselected. The reason is
XmCRCOLUMNHEADINGSELECT. Calldata
type is EwColumnHeading-
SelectionCallbackData.
columns: CSG #() An Array of EwTableColumns that specifies
the columns to be used to display
information about each item in the table.

508 IBM Smalltalk: Programmers Reference


Table 109. EwTableList resource and callback methods (continued)
editable: CSG false Specifies whether the cells in the table are
editable. For a cell to be editable, the table
widget must be editable, the column must be
editable, and the application must hook the
beginEditCallback and set the calldata doit
flag to true.
headingSeparator- CSG 1 Specifies the thickness of the horizontal line
Thickness: separating the column headings form the rest
of the table.
items: CSG OrderedCollection new A collection that represents the items in the
list.
itemWidth G 1 Specifies the width in pixels of items in the
list. The value of this resource will be set to
the sum of the widths of the columns plus
some number of pixels per column for
emphasis and separator. This resource is used
to set the scrolling interval for the horizontal
scroll bar. It is ignored if the scrollHorizontal
resource is false.
lockedColumns: CSG 0 Specifies the number of columns to be locked
on the left side of the table. For instance, a
value of 2 means that the first 2 columns in
the table is locked. A column can be locked
only if its preceding column is also locked.
When scrolling, the locked columns remain
fixed on the left side and all other columns
scroll under the locked columns. This value
must be no greater than the total number of
columns.
rowSeparators: CSG false Specifies whether the rows are separated by a
horizontal line.
selectableColumns: CSG false Specifies whether users might select columns
by clicking on their headings.
selectedCell G nil Represents a Point whose x component is the
index of the column of the selected cell and
whose y component is the index of the item
(row) of the selected cell. If nil, then no cell is
selected. Note: This resource is only relevant
if the selection policy is
XmCELLSINGLESELECT.
selectedCells G nil Represents a Collection of Points, each of
whose x component is the index of the
column of the selected cell and whose y
component is the index of the item (row) of
the selected cell. If empty, then no cell is
selected. Note: This resource is only relevant
if the selection policy is
XmCELLSINGLESELECT or
XmCELLEXTENDEDSELECT.
selectedColumns: CSG #() An Array of EwTableColumn that specifies
the currently selected columns. The columns
given must all be members of the columns
resource.

Appendix B. Extended widgets resources and callbacks 509


Table 109. EwTableList resource and callback methods (continued)
selectionPolicy: CSG XmBROWSESELECT Defines the interpretation of the selection
action. Allowed values are as follows:
XmSINGLESELECT Allows only single
selections. XmMULTIPLESELECT Allows
multiple selections. XmEXTENDEDSELECT
Allows extended selections.
XmBROWSESELECT Allows drag and
browse functionality XmREADONLYSELECT
Allows navigation, but no selection or
callbacks. XmCELLSINGLESELECT Allows
single selection of cells. If the widget and the
column are editable, then selecting a cell
causes it to be edited.
separatorsToExtremes: CSG false Specifies whether row and column separators
are to be extended to the extreme right and
bottom edges of the table.
visualStyle: CSG XmFLAT Defines how cells are displayed. Allowed
values are as follows: XmFLAT Looks like a
list box. XmETCHROWS Each row is etched.
XmETCHCELLS Each cell in an etched
column is individually etched. Note: Setting
the visual style after the widget is displayed
causes the background color to be set to the
OS defaults for the visual style given. This
means that to override this default, you
might need to set the background color
again.
EwTableList also inherits resource and callback methods from EwTableList, EwContainerList, EwLinearList, EwList,
EwScrollable, CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 110. EwTableTree resource and callback methods


childrenCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an items list of children is
needed. The callback reason is
XmCRCHILDREN. The application must
set the calldata children to the collection
of children objects of the item in calldata
item. Calldata type is
EwChildrenCallbackData.
expandCollapseCallback: C OrderedCollection new Specifies the list of callbacks that is
called when an item is expanded or
collapsed. The callback reason is
| XmCREXPAND or XmCRCOLLAPSE.
Calldata type is EwListCallbackData.
hierarchyPolicy: CSG EwHierarchyPolicy new An instance of EwHierarchyPolicy (or a
subclass) that defines how to display and
manage the hierarchy.
itemCount G 0 Specifies the total number of items. This
number might not match the items
resource because this number is the size
of the expanded hierarchy. It is
automatically updated by the list
whenever an element is added to or
deleted from the list.

510 IBM Smalltalk: Programmers Reference


Table 110. EwTableTree resource and callback methods (continued)
items: CSG OrderedCollection new A collection that represents hierarchy
root items in the list.
EwTableTree also inherits resource and callback methods from EwTableTree, EwTableList, EwContainerList, EwLinearList,
EwList, EwScrollable, CwExtendedPrimitive, CwExtendedWidget, and CwWidget.

Table 111. EwWINNotebook resource and callback methods


tabHeight: CG 25 The height of the page tabs in pixels.
tabsPerRow: CSG 3 The number of pages tables displayed in a
single row.
EwWINNotebook also inherits resource and callback methods from EwWINNotebook, EwNotebook,
CwExtendedComposite, CwExtendedWidget, and CwWidget.

Appendix B. Extended widgets resources and callbacks 511


512 IBM Smalltalk: Programmers Reference
Appendix C. Drag and drop resources and callbacks
This section provides a detailed listing of the resources and callbacks for each class
in the Drag and Drop subsystem. Note that only the set resource methods are
shown in each table, unless a resource only has a get method. Get methods have
the same name without the colon. Create/Set/Get (CSG) access designation and
default value are listed for each resource and callback.

The example Table 1. CwArrowButton resource and callback methods in Appendix A.


Widget resources and callbacks on page 469 shows how to use the tables in this
appendix.
Table 112. EwDragAndDropAdapter resource and callback methods
dragCancelCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the a drag has been canceled while the
receivers widget is either the source or target
of a drag. The reason is
XmCRDRAGCANCEL. Calldata type is as
follows: EwSourceCancelCallbackData for a
cancel when the widget is the source.
EwTargetCancelCallbackData for a cancel
when the widget is the target.
EwDragAndDropAdapter also inherits resource and callback methods from EwDragAndDropAdapter and Object.

Table 113. EwSourceAdapter resource and callback methods


dragChangeCallback: C OrderedCollection new Specifies a list of callbacks that is called when
the operation or target has changed during a
drag from the receivers widget. The reason is
XmCRDRAGCHANGE. Calldata type is
EwDragChangeCallbackData.
dragCompleteCallback: C OrderedCollection new Specifies a list of callbacks that is called when
a drag of items from the receivers widget
has completed; that is, the items have been
dropped elsewhere. The callback reason is
XmCRDRAGCOMPLETE. Calldata type is
EwDragCompleteCallbackData.
dragStartCallback: C OrderedCollection new Specifies a list of callbacks that is called when
a drag and drop is started in the receivers
widget. The callback reason is
XmCRDRAGSTART. Calldata type is
EwDragStartCallbackData.
EwSourceAdapter also inherits resource and callback methods from EwSourceAdapter, EwDragAndDropAdapter and
Object.

Table 114. EwTargetAdapter resource and callback methods


dragLeaveCallback: C OrderedCollection new Specifies a list of callbacks that is called when
items that were being dragged over the
receivers widget are dragged away from the
receivers widget. The callback reason is
XmCRDRAGLEAVE. Calldata type is
EwDragLeaveCallbackData.

Copyright IBM Corp. 1994, 2000 513


Table 114. EwTargetAdapter resource and callback methods (continued)
dragOverCallback: C OrderedCollection new Specifies a list of callbacks that is called when
items are being dragged over the receivers
widget. The callback reason is
XmCRDRAGOVER. Calldata type is
EwDragOverCallbackData.
dropCallback: C OrderedCollection new Specifies a list of callbacks that is called when
items are dropped on the receivers widget.
The callback reason is XmCRDROP. Calldata
type isEwDropCallbackData.
EwTargetAdapter also inherits resource and callback methods from EwTargetAdapter, EwDragAndDropAdapter and
Object.

EwTargetAdapter also inherits resource and callback methods from EwTargetAdapter


(Table 114 on page 513), EwDragAndDropAdapter (Table 112 on page 513) and Object.

514 IBM Smalltalk: Programmers Reference


Appendix D. Common graphics platform differences
Parts of the Common Graphics subsystem can behave differently depending on
constraints of the platform (hardware, operating system, and window system). For
example, Windows provides only four dashed-line styles and does not support
user-defined dash styles. Where possible, Common Graphics features are mapped
to the closest available features on the platform.

The tables below identify the platform constraints of the Common Graphics
subsystem. Blank cells indicate that the corresponding item is fully supported for
the indicated platform
Table 115. Constraints on cursors (CgCursor)
Item DOS/Windows OS/2 PM X/MOTIF
Color cursors Not supported Not supported

Table 116. Constraints on device independent images (CgDeviceIndependentImage)


Item DOS/Windows OS/2 PM X/MOTIF
Display of 1-bit images
Display of 4-bit images
Display of 8-bit images
Display of 16-bit images Not supported Not supported Not supported
Display of 24-bit images Not supported
Display of 32-bit images Not supported Not supported Not supported

Table 117. Constraints on graphics context attributes (CgGC)


Item DOS/Windows OS/2 PM X/MOTIF
arcMode
background
capStyle CapRound only
clipMask
dashes Closest match done to 4 Closest match done to 7
predefined dash lists: #(4 predefined dash lists: #(1
4), #(24 8), #(12 8 4 8), #(12 1), #(4 4), #(8 8), #(12 8 4 8),
4 4 4 4 4) #(12 8), #(4 4 4 20), #(12 4 4
4 4 4)
fillRule
fillStyle Tiles/stipples not
supported by thick lines.
FillStippled draws the same
as FillOpaqueStippled for
filled arcs.
font
foreground
function
joinStyle JoinRound only

Copyright IBM Corp. 1994, 2000 515


Table 117. Constraints on graphics context attributes (CgGC) (continued)
Item DOS/Windows OS/2 PM X/MOTIF
lineStyle Dashes not supported by Dashes not supported by
thick lines. Lines can have thick lines. Lines can have
either dashes or stipple/tile either dashes or stipple/tile
pattern, but not both. Fill pattern, but not both. Fill
style has priority. Color not style has priority.
supported by LineDoubleDash not
LineOnOffDash. supported.
lineWidth
stipple No limit for filled Foreground and
rectangles. 8x8 only for background colors
other graphics primitives. unsupported for stipples
larger that 8x8.
subwindowMode
tile No limit for filled
rectangles. 8x8 only for
other graphics primitives.

Table 118. Constraints on icons (CgIcon)


Item DOS/Windows OS/2 PM X/MOTIF
Icon size limit 32x32 32x32 (VGA) and 40x40 40x40 (8514/A, XGA)
(8514/A, XGA)

Table 119. Constraints on indexed palettes (CgIndexedPalette)


Item DOS/Windows OS/2 PM X/MOTIF
Animating entries Only 256-color devices Not supported Not supported

516 IBM Smalltalk: Programmers Reference


Appendix E. Common widgets platform differences
Parts of the Common Widgets subsystem can behave differently depending on
constraints of the platform (hardware, operating system, and window system). For
example, Windows supports only one alignment for text in button widgets. Where
possible, Common Widgets features are mapped to the closest features available on
the platform.

The tables below identify the platform constraints of the Common Widgets
subsystem. Blank cells indicate that the corresponding item is fully supported for
the indicated platform.
Table 120. General constraints
Item DOS/Windows OS/2 PM X/MOTIF
Border width The borderWidth resource The borderWidth resource
can only be 0 or 1 pixels. can only be 0 or 1 pixels.
Background and Only solid colors are
foreground color supported. Colors are not
dithered. Limited to the 20
system colors.

Table 121. Constraints on arrow button widgets. (CwArrowButton)


Item DOS/Windows OS/2 PM X/MOTIF
Sizing Displays the message
Arrow too big when the
widget is grown such that
the bitmap that is used to
draw the arrow becomes
larger than 64K. This does
not happen on Windows
95.
Border width The borderWidth resource is
ignored.

Table 122. Constraints on button and label widgets. (CwLabel, CwPushButton, CwToggleButton, CwCascadeButton,
CwDrawnButton)
Item DOS/Windows OS/2 PM X/MOTIF
Alignment The alignment resource is The alignment resource is
ignored for CwPushButton ignored for CwPushButton
and CwToggleButton. The and CwToggleButton. The
label is placed by the label is placed by the
operating system. operating system.
Margins The following resources The following resources
affect only the total width affect only the total width
and height of the widget, and height of the widget,
not the positioning of the not the positioning of the
label or pixmap inside the label or pixmap inside the
widget: marginBottom, widget: marginBottom,
marginHeight, marginLeft, marginHeight, marginLeft,
marginRight, marginTop, marginRight, marginTop,
marginWidth. marginWidth.

Copyright IBM Corp. 1994, 2000 517


Table 122. Constraints on button and label widgets (continued). (CwLabel, CwPushButton, CwToggleButton,
CwCascadeButton, CwDrawnButton)
Item DOS/Windows OS/2 PM X/MOTIF
Default shadow width The showAsDefault resource The showAsDefault resource
can only be 0 or 1. can only be 0 or 1.
Border width The borderWidth resource is
ignored.
As menu items The following are not The following are not
supported: backgroundColor, supported: backgroundColor,
foregroundColor, tab foregroundColor, tab
traversal and focus control traversal and focus control
(setInputFocus, (setInputFocus,
navigationType, traverseOn). navigationType, traverseOn).
Geometry requests and the Geometry requests and the
geometry values are geometry values are
undefined. The following undefined. The following
methods do nothing: methods do nothing:
stacking order requests stacking order requests
(bringToFront), event (bringToFront), event
handlers (even though they handlers (even though they
can be hooked), can be hooked),
deferRedraw:, and deferRedraw:, and
updateWidget. updateWidget.
Color Cannot set backgroundColor
or foregroundColor.
Focus A CwToggleButton with
indicatorType
XmONEOFMANY forces
set to true when it takes
focus.

Table 123. Constraints on combo-box widgets. (CwComboBox)


Item DOS/Windows OS/2 PM X/MOTIF
Event handlers Event handlers can be Event handlers can be
hooked on the combo-box hooked on the combo-box
widget but not on the widget but not on the
children that implement the children that implement the
combo box. combo box.
Editing When the comboBoxType When the comboBoxType
resource is XmSIMPLE, the resource is XmSIMPLE, the
combo box is always combo box is always
editable. The editable editable. The editable
resource has no effect. resource has no effect.
Default selection When the comboBoxType
resource is
XmDROPDOWN and there
is no initial string setting,
the first item in the combo
box is selected when the
combo box is dropped.

518 IBM Smalltalk: Programmers Reference


Table 124. Constraints on list widgets. (CwList)
Item DOS/Windows OS/2 PM X/MOTIF
Selection policy XmSINGLESELECT XmSINGLESELECT
behaves the same as behaves the same as
XmBROWSESELECT. XmBROWSESELECT.
Size limit There is a limit on the There is a limit on the
number of list items that number of list items that
the list will display. the list will display.
Automatic scrolling Positioning the list (setting Positioning the list (setting
the top and bottom item) the top and bottom item)
may not actually scroll the may not actually scroll the
list. This is because only list. This is because only
one blank line is allowed at one blank line is allowed at
the bottom of the list and, the bottom of the list and,
therefore, depending on the therefore, depending on the
number of items, the height number of items, the height
of the list, and the desired of the list, and the desired
position, the list may not position, the list may not
scroll. scroll.
Scroll bars When created normally (not When created normally (not
as a scrolled list), no scroll as a scrolled list), a vertical
bars appear. scroll bar appears.

When created as a scrolled When created as a scrolled


list, the vertical scroll bar is list, the vertical scroll bar is
visible only when required. always visible.

The horizontal scroll bar is The horizontal scroll bar is


visible when the visible when the
scrollHorizontal resource is scrollHorizontal resource is
true and the list contains an true.
item that is wider than the
width of the list.
Border width The widget is always
displayed with a 1-pixel
border.

Table 125. Constraints on main window widgets. (CwMainWindow)


Item DOS/Windows OS/2 PM X/MOTIF
Organization Must be created as the Must be created as the
child of a shell. child of a shell.

Appendix E. Common widgets platform differences 519


Table 125. Constraints on main window widgets (continued). (CwMainWindow)
Item DOS/Windows OS/2 PM X/MOTIF
Scroll bar limitations Scroll bar children have the Scroll bar children have the
following limitations: the following limitations: the
help callback is ignored, tab help callback is ignored, tab
traversal and focus control traversal and focus control
(setInputFocus, (setInputFocus,
navigationType, traverseOn) navigationType, traverseOn)
are not supported, are not supported,
geometry requests are geometry requests are
ignored, and the initial ignored, and the initial
geometry values are geometry values are
undefined. The following undefined. The following
methods do nothing: methods do nothing:
stacking order requests stacking order requests
(bringToFront), event (bringToFront), event
handlers (even though they handlers (even though they
can be hooked), can be hooked),
deferRedraw:, and deferRedraw:, and
updateWidget. updateWidget.

Table 126. Constraints on menus and menu bar. (CwRowColumn with rowColumnType of XmMENUBAR,
XmMENUPULLDOWN, or XmMENUPOPUP)
Item DOS/Windows OS/2 PM X/MOTIF
Types of child widgets Only CwLabel, Only CwLabel,
CwToggleButton, CwToggleButton,
CwSeparator, or CwSeparator, or
CwCascadeButton can be CwCascadeButton can be
added. added.
Help callback Not supported Not supported
Background color Not supported Not supported
Tab traversal and focus Not supported Not supported
control (setInputFocus:,
interceptEvents:, grabPointer:,
ungrabPointer:,
navigationType, traverseOn)
Geometry requests Geometry requests are Geometry requests are
ignored, and the initial ignored, and the initial
geometry values are geometry values are
undefined. undefined.
Stacking order Stacking order requests Stacking order requests
(bringToFront) do nothing. (bringToFront) do nothing.
Event handlers Can be hooked but do Can be hooked but do
nothing. nothing.
Updating widgets The updateWidget method The updateWidget method
does nothing. does nothing.
Ignored resources The following resources are The following resources are
ignored: adjustLast, ignored: adjustLast,
borderWidth, entryAlignment, borderWidth, entryAlignment,
entryBorder, marginHeight, entryBorder, marginHeight,
marginWidth, numColumns, marginWidth, numColumns,
orientation, packing, spacing. orientation, packing, spacing.

520 IBM Smalltalk: Programmers Reference


Table 126. Constraints on menus and menu bar (continued). (CwRowColumn with rowColumnType of
XmMENUBAR, XmMENUPULLDOWN, or XmMENUPOPUP)
Item DOS/Windows OS/2 PM X/MOTIF
Accelerator text with When a child has a When a child has a
pixmap menu items labelType of XmPIXMAP, its labelType of XmPIXMAP, its
acceleratorText is not shown. acceleratorText is not shown.
Drawn buttons CwDrawnButton children CwDrawnButton children
are not supported. are not supported.
Separators The separatorType, The separatorType,
orientation, and margin orientation, and margin
resources are ignored for resources are ignored for
CwSeparator children. CwSeparator children.

Table 127. Constraints on menu bar. (CwRowColumn with rowColumnType of XmMENUBAR)


Item DOS/Windows OS/2 PM X/MOTIF
Unmapping Unmapping the widget has Unmapping the widget has
the same visual effect as the same visual effect as
unmanaging. unmanaging.
Parent widget Can only be created as a Can only be created as a Can be created as a child of
child of a CwMainWindow. child of a CwMainWindow. any composite or shell
widget, but is usually a
child of a CwMainWindow.

Table 128. Constraints on menu. (CwRowColumn with rowColumnType of XmMENUPULLDOWN or


XmMENUPOPUP)
Item DOS/Windows OS/2 PM X/MOTIF
Redrawing The redraw and The redraw and
redraw:y:width:height: redraw:y:width:height:
methods do nothing. methods do nothing.

Table 129. Constraints on scale widget. (CwScale)


Item DOS/Windows OS/2 PM X/MOTIF
Event handlers Event handlers can be Event handlers can be
hooked on the scale widget hooked on the scale widget
but not on the children that but not on the children that
implement the scale. implement the scale.

Table 130. Constraints on scrolled bar widget. (CwScrollBar)


Item DOS/Windows OS/2 PM X/MOTIF
Border width Ignored Ignored

Table 131. Constraints on scrolled window widget. (CwScrolledWindow)


Item DOS/Windows OS/2 PM X/MOTIF
Organization The work area must be a The work area must be a A CwDrawingArea is
child of the scrolled child of the scrolled inserted between the
window. window. scrolled window and the
work area when scrolling
policy is XmAUTOMATIC.

Appendix E. Common widgets platform differences 521


Table 131. Constraints on scrolled window widget (continued). (CwScrolledWindow)
Item DOS/Windows OS/2 PM X/MOTIF
Scroll bar limitations Scroll bar children have the Scroll bar children have the
following limitations: the following limitations: the
help callback is ignored, tab help callback is ignored, tab
traversal and focus control traversal and focus control
(setInputFocus, (setInputFocus,
navigationType, traverseOn) navigationType, traverseOn)
are not supported, are not supported,
geometry requests are geometry requests are
ignored and the initial ignored and the initial
geometry values are geometry values are
undefined. The following undefined. The following
methods do nothing: methods do nothing:
stacking order requests stacking order requests
(bringToFront), event (bringToFront), event
handlers (even though they handlers (even though they
can be hooked), can be hooked),
deferRedraw:, and deferRedraw:, and
updateWidget. updateWidget.

Table 132. Constraints on text widget. (CwText)


Item DOS/Windows OS/2 PM X/MOTIF
Highlight vs selection Highlight and selection are the Highlight and selection are the
same. It is not possible to set the same. It is not possible to set
highlight without affecting the the highlight without affecting
selection. the selection.
Highlight appearance XmHIGHLIGHTSELECTED and XmHIGHLIGHTSELECTED
XmHIGHLIGHT- and XmHIGHLIGHT-
SECONDARYSELECTED both SECONDARYSELECTED both
appear as inverse text. appear as inverse text.
Insertion vs selection Insertion and selection are the Insertion and selection are the
same. It is not possible to move same. It is not possible to
the insertion point without move the insertion point
affecting the selection. without affecting the selection.
Insertion point The insertion point The insertion point
(cursorPosition, (cursorPosition,
getInsertionPosition) is always getInsertionPosition) is always
answered as the beginning of the answered as the beginning of
selection if the editMode is the selection if the editMode is
XmSINGLELINEEDIT. XmSINGLELINEEDIT.
Visual appearance The selection is hidden when
the widget loses focus.
Background and Only solid background colors
foreground color are supported.

Table 133. Constraints on top-level shell and dialog shell. (CwTopLevelShell, CwDialogShell)
Item DOS/Windows OS/2 PM X/MOTIF
Window titles Centered Left Centered

522 IBM Smalltalk: Programmers Reference


Table 133. Constraints on top-level shell and dialog shell (continued). (CwTopLevelShell, CwDialogShell)
Item DOS/Windows OS/2 PM X/MOTIF
Behavior of mwmDecorations No title bar is included A title bar is included if any
resource unless one of the following is set:
1. A title bar is always included MWMDECORTITLE is MWMDECORTITLE,
(MWMDECORTITLE). set. MWMDECORMENU,
2. If MWMDECORMAXIMIZE MWMDECORMINIMIZE, or
or MWMDECORMINIMIZE MWMDECORMAXIMIZE.
is set, MWMDECORRESIZEH
is added.
3. If a menu bar is added to a
shell that has
MWMDECORBORDER,
Windows does not paint the
shell properly.

Appendix E. Common widgets platform differences 523


524 IBM Smalltalk: Programmers Reference
Index
A callbacks 146 (continued)
extended widgets 203
CGIndexedPalette
constraints 515
accelerator keys 171 focus 158, 163 CgLogicalFontDescription 82, 93, 98
accepting drops 219 input 164 CgPalette 115
adapters 263 methods 153 CgPCXFileFormat 127
ambient-properties 462 notebooks and pages 250 CgPixmap 82, 106, 119, 133
application-drawn button widgets 177 resize 164 cursors 103
application shell widgets 158 selection 186, 189 CgPMBMPFileFormat 127
arcs simple 180 CgPMICOFileFormat 127
drawing 92 valueChanged 163, 174 CgRegion 82
filled 93 canceling a drag 269 CgRGBColor 113, 133, 213
Array 8, 10 cascade-button widgets 171, 178 CgScreen 81
Association 14 CFS 1, 53 colors 114
asynchronous CfsConstants 54 default object 81
callouts 298 CfsDirectoryDescriptor 54, 55, 58 icons 125, 126
messages (interrupts) 327 CfsDirectoryEntry 54, 59 CgSegment 82, 133
attributes CfsError 54, 71 drawing with 90
print job 276 platform-specific errors 76 CgTextItem 93, 133
testing for specific errors 73 drawing with 101
CfsFileDescriptor 54, 55, 64, 67, 68, 76 CgTIFFFileFormat 127
B CfsFileStream 61, 67 CgVisual 82
backgroundColor 213 CfsReadFileStream 61 CgWinBMPFileFormat 127
Bag 8, 10, 12 CfsReadWriteFileStream 61 CgWindow 81, 82
Behavior class 34 CfsStat 54, 74 CgWinICOFileFormat 127
bitmaps 106, 109 CfsVolumeInfo 54 Character 14
Block 25, 48, 49 CfsWriteFileStream 61 Characters
Boolean 7 CG 2, 79 double-byte 14
building CgArc 82, 133 check-box widgets 175
DDE clients 357 CgCharStruct 82, 93, 99, 133 child 135
DDE servers 344 CgConstants 83 circles
button tools 260 CgCursor 133 drawing 92
button widgets 171 CgCursors filled 93
application-drawn 177 constraints on cursors 515 circular constraint error 168
cascade- 178 CgDeviceIndependentImage 120, 133 Class class 36
greying out 178 CgDirectPalette 115, 120, 122, 133 class hierarchies
iconic- 176 CgDisplay 81 Common Graphics 82
push- 172 cursors 103 Common Widgets 135, 137
radio- 174 default object 81 Extended Widgets 233
toggle- 173 fonts 93 classes
ButtonMenuMask 183 CgDrawable 81 core Common Graphics 80
ByteArray 8, 10 CgFileFormat 127, 131 dynamic data exchange 342
CgFont 93, 98, 133 seldom-used and abstract classes 82
cursors 103 CLDT 1, 5
C freeing 102
loading 98
CldtConstants 15
client requests, handling 351
C language CgFontProp 82, 93, 133 CLIM 1, 33
Common Graphics conversion CgFontStruct 93, 98, 133 clipboard uses 213
from 79 assigning to graphics context 101 coldlink events 356
Common Widgets conversion loading 98 coldlinking items 361
from 135 methods 99 Collection 8
IBM C Set/2 compiler 333 unloading 102 common protocols 10
interfacing to 285 CgGC 82, 133 colors 113
programming model 286 default object 82 color-specification string 114
sample callback for OS/2 330 fonts 93 looking up values 114
supported parameter types 288 CgGCValues 102, 133 of widgets 213
writing user primitives 333 using 85 setting background in GC 88
callbacks 146 CgIcon 124, 134 setting foreground in GC 88
activate 146, 150, 172 CgIconFileFormat 127 specifying by name 113
adding 277 CgID 82 specifying by RGB 113
DDE events 362 CgImageFileFormat 127 combo-box widgets 189
destroy 146, 164 CgIndexedPalette 115, 133
expose 164

Copyright IBM Corp. 1994, 2000 525


commands Common Process Model (continued) CwArrowButton 136 (continued)
executing at a server 362 ending processes 46 constraints 517
Common File System 53, 58 overview 1, 45 methods 469
classes in 54, 61 process methods 49 CwBasicWidget 136
constants 54 process priorities 46 methods 469
error handling 71 ProcessorScheduler methods 50 CwBulletinBoard 190, 197
file streams 61 resuming processes 46 methods 469
filenames and paths 54 Semaphore 47 CwCascadeButton 136, 171
files 64 Semaphore methods 51 constraints 517
line delimiter constants 64 suspending processes 46 methods 470
locking 68 synchronization 47 CwComboBox 136, 189
managing files and directories 55 common resources 235 constraints 518
mixing streams and file Common Widgets 82 methods 471
descriptors 67 class hierarchy 135 CwComposite 136
obtaining volume information 57 clipboard uses 213 methods 472
overview 1, 53 constants 144 CwCompositeBox 190
path separators 55 constraints 517 methods 472
platform-specific error handling 76 drag and drop 215 CwConstants 144
platform-specific operations 76 classes 217 CwDialogShell 158, 191, 193, 196
root directories 55 convert proc 224 constraints 522
share modes 70 drop proc 221 methods 473
testing file properties 74 drop site widgets 219 CwDragContext 218
Common File System, line delimiter transfer proc 225 CwDrawingArea 164
constants 64 OSF/Motif compatibility 135 methods 473
Common Graphics 79, 82 overview 2, 135 CwDrawnButton 136, 171
abstract classes 82 platform differences 517 constraints 517
class hierarchy 80 timer proc 230 methods 473
constants 83 work proc 230 CwDropSite 219
drawing process overview 83 CompiledMethod 42 CwDropTransfer 223
frequently used classes 80 Compiler 40 CwExtendedComposite
image support 112 CompilerError 40 methods 491
overview 2 composite widgets 136 CwExtendedPrimitive
platform differences 515 configuring systems 269 methods 491
printing with 279 constants CwFileSelectionPrompter 197
seldom-used classes 82 character 15 CwForm 197, 474
Xlib compatibility 79, 112 file-system 54 CwFrame
Common Language Data Types 5 graphics constants 83 methods 474
Boolean classes 7 platform constants 106 CwLabel 136, 171
collection classes 8 widget constants 144 constraints 517
collection protocols 9 constraints 515, 517 methods 474
graphical classes 20 convenience methods CwList 136, 185
magnitude classes 13 graphics contexts 88 constraints 519
object behavior 6 widget 148, 150 methods 476
overview 1, 5 convert proc 224 CwMainWindow 161
stream classes 22 converting Smalltalk objects to a constraints 519
support classes 24 ByteArray 372 methods 477
Common Language Implementation copying files 56 CwMessageBox 191, 196
behavior messages 34 CP 2, 273 methods 477
class messages 36 CPM 1, 45 CwMessagePrompter 197
CompiledMethod 42 creating CwOverrideShell 158
compiling and evaluating code 40 object verb menu 451 methods 477
creating new classes 38 OLE Automation Objects 458 CwPrimitive 136
EmSystemConfiguration 42 OLE clients 444 methods 477
extended messages 39 OLE main window 443 CwPrinterShell
metaclass messages 38 tools 259 resources and convenience
overview 1, 33 widgets 148 methods 279
Common Printing Creation convenience methods 234 CwPushButton 136, 142, 146, 171
classes 273 cursors constraints 517
complete example 280 color 105 methods 478
definition 273 for platform 106 CwRowColumn 169, 174, 175, 178
overview 2, 273 from glyphs 103, 104 constraints 520
Common Process Model from pixmaps 103, 105 methods 478
and Delay 47 overview and process 103 CwScale 481
block evaluation methods 48 predefined 104 constraints 521
block methods 49 CW 2, 135 CwScrollBar 136
creating processes 45 CwAccelerator 171 constraints 521
Delay methods 50 CwArrowButton 136 methods 481

526 IBM Smalltalk: Programmers Reference


CwScrolledWindow 160 dialogs 158, 190 (continued) dynamic data exchange 339 (continued)
constraints 521 question 196 building
methods 483 selection 197 DDE clients 357
CwSelectionBox 193, 197 warning 196 servers 344
methods 483 working 196 callbacks 362
CwSeparator Dictionary 8, 10 classes 342
methods 484 direct editing concepts and definitions 339
CwShell 136 cell values 245 formats of transferred data 362
methods 484 direct editing of labels 239 platform-specific support 374
CwText 136, 162 DirectedMessage 25 test cases 372
constraints 522 directories
methods 485 closing 61
CwTextPrompter 197
CwToggleButton 136, 171, 211, 486
copying 56
creating 55
E
editing
constraints 517 current working directory 55
cell values 245
CwTopLevelShell 138, 158 managing 55
labels 239
constraints 522 opening 58
policies 246
methods 487 path 57
EmSystemConfiguration 42
CwTransientShell reading 59
entry points 294
methods 487 removing 55
errors
CwWidget 82, 136 renaming 56
circular constraint errors 168
methods 487 searching 58
Common File System
CwWMShell Startup 57
CfsError 54
methods 489 disconnecting 372
Common Graphics image files 127
DLL 124
non-homogeneous 174
double-byte characters 9, 14
EsEntryPoint 294
D double-click
drawing area widget 166
EsWindowSystemStartUp 226
dashes event handling 146, 154
drag and drop
GC convenience method 88 event masks 154
adapters 263
data examples 146, 157, 166
callbacks 513
requesting 360 menu-button event 183
objects 217
Date 16 event polling 226
overview 2
DBString 8, 10 events
platform-integrated 215
DD 2 coldlink 356
pluggable 263
DDE 339 request 356
resources 513
DdeCallbackData class 343 run and poke 357
sequence of events 264
DdeClass termination 357
target types 216
public instance methods 367 warmlink and hotlink 355
drag proc 220
DdeClient EW 2, 233
drag source widget 217
discussion 359 EwConstants pool dictionary 233
drawing
protocols 371 EwContainerList
arcs 92
public instance methods 369 methods 492
circles 92
DdeClient class 342 EwDragAndDropAdapter
drawing process 83
DdeServer methods 513
filled arcs 92
protocols 371 EwDrawnList
filled polygons 92
public instance methods 368 methods 493
filled rectangles 91
DdeServer class 343 EwFlowedIconList
in color pixmaps 119
DdeServerManager methods 494
lines 90
discussion 352 EwGroupTool 261
pie slices with arcs 93
public class methods 364 EwIconArea
points 90
public instance methods 364 methods 494
polygons 92
DdeServerManager class 343 EwIconList
rectangles 91
decorations methods 496
setting state in GC 88
window 159 EwIconTree
strings 101
delay 228 methods 497
text 101
Delay 45, 47, 50 EwLabelTool 260
using palettes 118
device independent images EwLinearList
with color 117
constraints 515 methods 497
with fonts 100
dialogs 158, 190 EwList
with widgets 82, 164, 177
bulletin board 197 methods 498
drawn-button widgets 171
convenience methods 196 EwNotebook
drawn list 236
error 196 methods 499
drop proc 221
form 197 EwPage
drop site widgets 219
information 196 methods 500
dropping on a target 268
message 191, 196 EwPMNotebook
dynamic data exchange 339
modal 191 methods 501
prompt 197 EwProgressBarTool 260

Index 527
EwPushButtonTool 260 files (continued) GCJoinStyle 86
EwScrollable locking 68 GCLineStyle 86
methods 502 managing 55 GCLineWidth 85, 86
EwSeparatorTool 260 name length 57 GCSubwindowMode 86
EwSimpleGroupTool 262 names 57 GCTileStipXOrigin 86
EwSlider opening 64 GCTileStipYOrigin 86
methods 502 opening using share modes 71 geometry management 139, 161
EwSourceAdapter path 55 glyph cursors 104
methods 513 platform-specific calls 76 graphic constants 83
EwSpinButton portable names 54 graphics contexts 82
methods 505 properties 74 attributes of 86
EwTableColumn reading using low-level changing 89
methods 507 operations 66 constraints 515
EwTableList reading using streams 24, 61 convenience methods 88
methods 508 renaming 56 copying 88
EwTableTree rewind 67 creating 85
methods 510 seeking 66 freeing 89
EwTargetAdapter share modes 70 GCFont values 102
methods 513 size 67 methods for 84
EwToggleButtonTool 260 supported lock types 69 retrieving values 85
EwWINNotebook supported share modes 70 using 84
methods 511 with streams 67 valuemasks 85
ExAll 25 writing using streams 61 with other drawables 89
ExceptionalEvent 25, 26 Float 19 greying out widgets 178
collections 28 flowed icon lists 240
exceptions focus 158
completion-block 29
creating 25
fonts 93
as cursors 103
H
handling
examples 30 assigning to graphics context 101
client requests 351
handling 25, 26 drawing with 100
links 349
signaling 26 freeing 102
hierarchy, class 135
Extended class 39 from graphics context 102
hotlink events 355
Extended list widgets 234 loading 97
hotlinking to items 360
extended widgets 201, 233 overview and process 94
callbacks 203, 491 pattern matching 95
class hierarchy 233 querying 95
examples 203 scalable 97 I
initialization 202 used by widgets 211 IBM Smalltalk
overview 2 wildcard characters 95 architecture 3
resources 202, 491 form widgets 167 base subsystems 1
using 203, 206, 211 circular constraint errors 168 design intent 2
External language interface 288 formats overview 1
data platform support 4
DDE servers and clients 362 icon area widget 241
F Fraction 19
freeing
icon lists 237
icon trees 248
file formats 126
DDE clients or servers 372 icons 124, 126
file streams
fonts 102 constraints 515
classes 61
graphics contexts 89 creating 124
closing 61
pixmaps 107 drawing 125
line delimiters 63
summary 133 for application windows 159
locking 68
in buttons 176
of bytes 63
loading from a file 128
of characters 63
opening 61 G loading from ByteArray 130
loading from DLL 125
using access modes and flags 67 garbage collection 326
loading into a file 129
with file descriptors 67 GC 82
loading into ByteArray 130
writing 24 GCArcMode 86
OS 126
files GCBackground 86
IdentityDictionary 8, 10
changing offset 66 GCCapStyle 86
image file formats 126, 131
closing 65 GCClipMask 86
PCX 131
copying 56 GCClipXOrigin 86
PM BMP 132
deleting 56 GCClipYOrigin 86
TIFF 132
directory path case 57 GCDashOffset 86
Windows BMP 132
error handling 71 GCFillRule 86
images
existence 73, 74 GCFillStyle 86
copying from a drawable 123
file streams 61 GCFont 86
creating 121
flush 67 GCFunction 85, 86

528 IBM Smalltalk: Programmers Reference


images (continued) message-box widgets 191 methods 19, 39, 99, 486 (continued)
device independent 123 Metaclass class 38 allMethodsNamed: 36
device-independent 120 methods 19, 39, 99, 486 allMethodsReferencing
direct-color 122 - 17, 21 -InstVarName: 36
displaying 122 , 10 allMethodsReferencingLiteral: 36
loading from a file 127 / 17, 21 allMethodsSending: 36
loading from ByteArray 130 * 17, 21 allMethodsSendingAll: 36
loading into a file 129 & 7 allSelectors 35
loading into ByteArray 130 + 17, 21 allSharedPoolNames 37
manipulating 121 = 7, 8, 14, 21 allSubclasses 34
implementing server behavior 349 // 17, 21 allSubclassesBreadthFirstDo: 35
indexed palettes @ 17 allSubclassesDepthFirstDo: 35
constraints 515 == 7, 8 allSubclassesDo: 35
initiating DDE events 354 \\ 17 allSuperclasses 34
Integer 18 \< 12, 14, 21 allSuperclassesDo: 35
inter-process communication 425 \<= 12, 14, 21 amountToTranslateWithin: 22
interrupts (asynchronous messages) 327 > 12, 14, 21 and: 7
Interval 8, 10, 12 >= 12, 14, 21 answerString: 200
IPC 425 >> 35 anyMask: 18
| 28 arcCos 18
abs 17, 21 arcSin 18
K Accessing
fractionPart 20
arcTan 18
area 22
keyboard events 154
integerPart 20 areasOutside: 22
scale 20 argument 27
significantDigits 20 argumentCount 13, 48
L accessMode: 200 arguments 25, 27
label tools 260 activePriority 50 arguments: 25
label widgets 171 activeProcess 50 asArray 10
labels, direct editing 239 add: 10, 11 asBag 10
leaving a target 268 add:after: 13 asByteArray 10
limitations 270 add:afterIndex: 13 ascent 99, 100
line delimiters 22 add:before: 13 asCharacter 19
constants 64 add:beforeIndex: 13 asClassPoolKey 40
line delimiters, constants 15, 64 add:withOccurrences: 12 asDBString 13
lines addAll: 10, 11 asDecimal 19
drawing 90 addAll:after: 13 asFloat 17
drawLines:points:mode: 90 addAll:afterIndex: 13 asFraction 17
setting line attributes 88 addAll:before: 13 asGlobalKey 40
links, handling 349 addAll:beforeIndex: 13 asInteger 17
list widgets 185 addAllFirst: 13 asLowercase 12, 15
multiple selection 187 addAllLast: 13 asNumber 12
scrolled- 188 addCallback:receiver:- asOrderedCollection 10
single selection 186 selector:clientData: 150 asPoolKey 40
lists addClassVarName: 37 asSBString 13
drawn 236 addCompiledMethod: 36 asSeconds 16, 17
flowed icon 240 addDays: 16 asSet 10
icon 237 addDependent: 6 associationAt: 11
scrolled 235 addEventHandler:receiver:selector: associationAt:ifAbsent: 11
table 241 -clientData: 154 associationsDo: 11
LookupTable 8, 10 addFirst: 13 asSortedCollection 10
losingFocusCallback 163 addInstVarName: 37 asSortedCollection: 10
addLast: 13 asString 12, 15
addLineDelimiters 13 asSymbol 12, 15
M addProcess 47
address 291
asUppercase 12, 15
asyncExecInUI: 229, 231
magnitude comparing 14
addSharedPoolName: 37 at: 10, 11, 116, 120
main-window widgets 161
addTime: 17 at:ifAbsent: 11
managed pool dictionaries 396
addTimeOut:receiver:selector: at:ifAbsentPut: 11
menu bar 180
-clientData: 230 at:ifPresent: 8, 11
normal 184
addWorkProc:receiver:selector: at:put: 11, 12
simple 179
-clientData: 230 atAll:put: 12
menus
allClassVarNames 37 atAllPut: 12
normal 184
allInstances 35 atEnd 24
pop-up 183
allInstVarNames 37 atEndOrWhenExceptionDo: 29, 32
pull-down 180
allMask: 18 backgroundColor: 213
simple 179
allMethodsDo: 35 basicAllInstances 35
Message 25

Index 529
methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued)
basicAt: 10 comment: 37 createManagedWidget:parent:-
basicAt:put: 12 compile:inClass: 41 argBlock: 203
basicMultiAllInstances 40 compile:inClass:ifFail: 41 createMessageBox:argBlock: 191
basicNew 34 compile:inClass:warningLevel:- createMessageDialog:argBlock: 191,
basicNew: 34 onWarning:ifFail: 41 196
basicSize 7 compiledMethodAt: 35 createPixmap:height:depth: 107, 119
bearing 100 compiledMethodAt:ifAbsent: 35 createPixmapCursor:foreground-
become: 7 compiler 34 Color:backgroundColor:x:y: 105
between:and: 14, 21 compression 132 createPixmapCursor:x:y: 105
bindWith: 13 compression: 132 createPixmapFromBitmapData:-
bindWith:with: 13 configuredSubsystems 42 width:height:fg:bg:depth: 107
bindWith:with:with: 13 conform: 11 createPopupShell:
bindWith:with:with:with: 13 connectToSuper 38 -parent:argBlock: 149, 158
bindWithArguments: 13 contains: 22 createPrimaryWidget:parent:-
bitAnd: 18 containsPoint: 22 argBlock: 202
bitAt: 18 contents 23, 24, 62, 63, 72 createPromptDialog:argBlock: 197
bitInvert 18 context 40 createPushButton:argBlock: 172
bitOr: 18 Converting createQuestionDialog:argBlock: 196
bitShift: 18 asDecimal 20 createRadioBox:argBlock: 174
bitXor: 18 asFloat 20 createRowColumn:argBlock: 169
black 113 asFraction 20 createScrolledList:argBlock: 149, 185,
blackPixel 83, 119 asInteger 20 188
blue 113 copy 6 createScrolledText:argBlock: 162
bottom 22 copyArea:gc:srcX:srcY:width:- createScrolledWindow:argBlock: 160
bottom: 22 height:destX:destY: 108, 120 createSelectionBox:argBlock: 193
bottomCenter 22 copyFrom:to: 10, 23 createSelectionDialog:argBlock: 193,
bottomLeft 22 copyGC:dest: 84, 88 197
bottomLeft: 22 copyPlane:gc:srcX:srcY:width:- height:- createSimpleMenuBar:argBlock: 179
bottomRight 22 destX:destY:plane: 111 createSimplePopupMenu:
bottomRight: 22 copyReplaceAll:with: 10 -argBlock: 179
broadcast: 6 copyReplaceFrom:to:with: 10 createSimplePulldownMenu:
broadcast:with: 6 copyReplaceFrom:to:withObject: 10 -argBlock: 179
button 156 copyReplacing:withObject: 10 createText:argBlock: 162
buttonType: 72, 198 copyright 43 createWarningDialog:argBlock: 196
call 292 copyWith: 10 createWidget:parent:argBlock: 203
callingConvention 291 copyWithout: 10 createWidgetSystem 202
callingConvention:address:- corner 22 createWorkingDialog:argBlock: 196
parameterTypes:returnType: 292 corner: 21, 22 Creating instances 20
callingConvention:function:library:- cos 18 critical: 51
parameterTypes:returnType: 292 count 155 currentError 127, 129
callWith: 292 cr 24, 63 currentErrorString 127, 129
callWith:with: 292 createApplicationShell:argBlock: 149, data 121
callWith:with:...:with: 292 158 dateAndTimeNow 16, 17
canUnderstand: 36 createBitmapFromData:width:- dayIndex 16
ceiling 18 height: 105 dayName 16
center 22 createBitmapFromData:with:- dayOfMonth 16
changed 6 height: 109 dayOfYear 16
changed: 6 createBulletinBoardDialog:- daysFromBaseDay 16
changeGC:values: 84, 89 argBlock: 197 daysInMonth 16
character 156 createComboBox:argBlock: 189 daysInMonth:forYear: 16
chars: 101 createDrawingArea:argBlock: 164 daysInYear 16
chars:delta:font: 101 createErrorDialog:argBlock: 196 daysInYear: 16
chdir: 55 createFontCursor: 106 daysLeftInMonth 16
class 6 createForm:argBlock: 167 daysLeftInYear 16
classPool 37 createFormDialog:argBlock: 197 defaultButtonType: 199
classVarNames 37 createGC:values: 84, 85, 86 defaultChar 99
clearBit: 18 createGlyphCursor:sourceChar:- defaultHandler 25
close 23, 62, 65, 72, 293 maskChar: 104 defaultScreen 81
closedir 61 createGlyphCursor:sourceChar:- defineCursor: 103
collect: 11 maskChar:foregroundColor:- definitionString 37
colorCube: 122 backgroundColor: 104 degreesToRadians 17
colorEncode 132 createInformationDialog:- deleteAllSelectors: 36
colors: 116 argBlock: 196 deleteSelector: 36
colorScheme 132 createLabel:argBlock: 172 deleteSelector:ifAbsent: 36
colorsUsed 132 createList:argBlock: 185 delta: 101
comment 37 createMainWindow:argBlock: 161 denominator 19

530 IBM Smalltalk: Programmers Reference


methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued)
dependents 6 floorLog: 18 height 22, 99, 100, 109, 121, 143, 155,
depth 107, 109, 121 flush 24, 67 279
descent 99, 100 font 99 height: 22, 143
description 25, 27 font: 101 highBit 18
destroyIcon: 126 fontList 212 highIOPriority 46, 50
destroyWidget 140 fontList: 211 hours 17
detect: 11 fontName 212 hRes 131
detect:ifNone: 11 fontSettableWidgets 212 icon 159
digitValue 14 fontStruct: 211 iconMask 159
digitValue: 15 foregroundColor: 213 iconPixmap 159
disconnectFromSuper 38 fork 45, 48, 49 iconToUnload 129
display 155, 279 forkAt: 45, 47, 49 iconType: 72, 199
dist: 21 formatMatchesByteObjects:- ident 132
dName 59 offsetsIntoByteObjects: 131 identifier 72
do: 8, 11, 23 formatMatchesFileHandle: ifFalse: 7
doesNotUnderstand: 7 -atOffset: 131 ifFalse:ifTrue: 7
dotProduct: 21 formatMatchingByteObjects:- ifTrue: 7
doWithIndex: 11 offsetsIntoByteObjects:ifNone: 131 ifTrue:ifFalse: 7
drawArc:x:y:width:height:- formatMatchingFileHandle:- imageStorage 130
angle1:angle2: 89, 92 atOffset:ifNone: 131 importantColors 132
drawArcs:arcs: 89, 92 forMilliseconds: 50 importantColors: 132
drawIcon:x:y:icon: 125 forMutualExclusion 51 includes: 12
drawImageString:x:y:string: 89, 101 forSeconds: 50 includesKey: 11
drawLine:x1:y1:x2:y2: 89, 90 fractionPart 19 includesSelector: 36
drawLines:points:mode: 89 freeCursor 103, 133 increment 12
drawPoint:x:y: 89, 90 freeFont 102, 133 indexOf: 10
drawPoints:points:mode: 89, 90 freeGC 84, 89, 133 indexOf:ifAbsent: 10
drawRectangle:x:y:width:height: 89, freeIcon 124, 133 indexOf:matchCase:startingAt: 12
91 freePixmap 107, 133 indexOfDay: 16
drawRectangles:rectangles: 89, 91 from:to: 12 indexOfMonth: 16
drawSegments:segments: 89, 90 from:to:by: 12 indexOfSubCollection:- startingAt: 10
drawString:x:y:string: 89, 101 from:to:do: 11 indexOfSubCollection:-
drawText:x:y:items: 89, 93, 101 from:to:doWithIndex: 11 startingAt:ifAbsent: 10
dropSiteRegister: 219 fromDays: 16 inheritsFrom: 34
equals: 42 fromImage:maskImage: 124 initialize 38, 202
eqv: 7 fromOSIcon: 126 initializeAfterCreate 202
errno 72 fromResource: 125 initializeResources 202
error: 7, 25 fromResource:fileName: 126 inject:into: 11
evaluate: 41 fromSeconds: 17 insetBy: 22
evaluate:for: 41 fromSystem: 126 insetOriginBy:cornerBy: 22
evaluate:for:ifFail: 41 function: 85 instSize 35
evaluate:for:warningLevel:- functionNumber 291 instVarAt: 7
onWarning:ifFail: 41 gcd: 19 instVarAt:put: 7
even 18 getAddress: 293 instVarNames 37
exception 27, 29 getChild: 192 integerPart 19
execLongOperation: 47 getcwd 55 intensity: 113, 116
exit 43 getDeviceIndependentImage: 123 intersect: 22
exitWith: 27, 29 getGCValues: 102 intersects: 22
exp 18 getGCValues:valuesReturn: 84 isAlphaNumeric 16
expandBy: 22 getGeometry: 108 isBits 35
extent 22, 100, 121 getIcon:foregroundColor: 126 isBitSet: 18
extent: 21, 22 getPalette 118 isBlk 74
factorial 19 getPixel:y: 122 isBytes 35, 63
fileDescriptor 68, 76 getPixels:y:width:pixels:- isBytes: 63
fileName: 200 startIndex: 122 isCfsError 53, 71, 73
fillArc:x:y:width:height:angle1:- getsInstVar: 42 isCharacter 6
angle2: 89, 92 getState 174 isCharacters 63
fillArcs:arcs: 89, 92 getString 162, 189 isCharacters: 63
fillPolygon:points:shape:mode: 89, 92 getSubImage: 122 isChr 74
fillRectangle:x:y:width:height: 89, 91 green 113 isClass 6
fillRectangles:rectangles: 89, 91 halt 7 isDBString 6
findFirst: 10 halt: 7 isDigit 16
findLast: 10 handlesByDefault 27 isDir 74
first 10 hasErrorOccurred 127, 129 isEmpty 12, 24
firstDayOfMonth 16 hash 7 isFifo 74
floor 18 hasMethods 36 isFixed 35

Index 531
methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued)
isFloat 6 mask:keysym: 171 open:oflag:share: 68, 71
isInteger 6 match: 12 opendir:pattern:mode: 58
isKindOf: 6 max: 14, 21 openEmpty: 61, 71
isLetter 16 maxBounds 99 or: 7
isLowercase 16 merge: 22 origin 22
isMemberOf: 6 message 40, 72 origin: 22
isMetaclass 6 messageLoop 226 origin:corner: 22
isNil 6, 24 messageString: 72, 198, 200 origin:extent: 22
isPointers 35 methodClass 42 pageNumber 279
isPrimitive 42 methodClass: 42 palette 121
isReg 74 methodDictionary 35 paletteInfo 131
isSBString 6 methodsDo: 35 parameterTypes 291
isSeparator 16 millisecondClockValue 17 parent 25
isSpecial 74 millisecondsPerDay 17 parseColor: 114
isString 6 millisecondsToRun: 17 pathSeparator 55
isSymbol 6 min: 14, 21 pathSeparatorString 55
isUppercase 16 minBounds 99 peek: 24
isUserPrimitive 42 minutes 17 peekFor: 24
isVariable 35 mkdir: 55 pelsPerMeter 132
isVowel 16 monthIndex 16 pelsPerMeter: 132
jobAttributes 279 monthName 16 perChar 99
key 14 moreGeneralThan: 18 perCharAtRow: 99
key: 14 moveBy: 22 perCharNumColumns 99
key:value: 14 moveTo: 22 perCharNumRows 99
keyAtValue: 11 MultiAllInstances 40 perform: 7
keyAtValue:ifAbsent: 11 multiBecome: 12 perform:with: 7
keys 11 Multiple Instance Accessing 40 perform:with:with: 7
keysAndValuesDo: 11 mwmDecorations 159 perform:with:with:with: 7
keysDo: 11 name 34, 99, 291 perform:withArguments: 7
keysym 156 nameOfDay: 16 physicalName 293
labelString: 171 nameOfMonth: 16 pi 19
last 10, 55 nearestColor: 117, 120 platformErrno 76
lbearing 100 nearestPixelValue: 117, 120 platformErrorCategory 76
lcm: 19 negated 17, 21 platformErrorLocation 76
left 22 negative 17 platformRecommendedAction 76
left: 22 new 10, 51 point 156
left:right:top:bottom: 22 new: 10 pointRoot 156
leftCenter 22 newChild 25 position 24
lessGeneralThan: 18 newDay:month:year: 16 position: 24
library 291 newDay:monthIndex:year: 16 positive 17
lineDelimiter 23, 63 newDay:year: 16 predictor 132
lineDelimiter: 23, 63 newProcess 45, 46, 49 primaryInstance 38
lineWidth: 85 newProcessWith: 45, 49 primaryInstance: 38
listFonts: 97 next 24, 63 primaryWidget 202
listFonts:maxnames: 95 next: 24 primitiveFailed 7
ln 18 next:put: 24 printOn: 6, 72, 138
loadFont: 98 nextLine 24, 63 printOn:base: 19
loadFromByteObjects:- nextMatchFor: 24 printOn:showDigits:Pad: 20
offsetsIntoByteObjects: 130 nextPut: 24, 63 printString 6, 72
loadFromFile: 127, 128 nextPutAll: 24, 62, 63 printStringRadix: 19
loadFromFileHandle:atOffset: 127, noMask: 18 printStringRadix:padTo: 19
128 normal 21 printStringRadix:showRadix: 19
loadQueryFont: 98, 211 not 7 priority 46, 49
lock:start:len: 69 notEmpty 12 priority: 46, 47, 49
log: 18 notNil 6, 24 prompt 72, 198, 199, 200
logicalName 293 now 17 prompterStyle: 198
logicalName: 293 nullTerminated 12 putDeviceIndependentImage:-
lookupColor: 114, 116 numChars 99 image:srcRect:destRect: 122
lowIOPriority 46, 50 numerator 19 putPixel:y:pixelValue: 121
lseek:whence: 66 numerator:denominator: 19 putPixels:y:width:pixels:
manageChild 139 occurencesOf: 12 -startIndex: 122
mapFontCursor:platform- odd 18 queryBestIconSize 125
Cursor: 106 on: 24, 67, 76 queryFont 102
mapLogicalName:toPhysical- on:from:to: 24 queueInterrupt: 49
Name: 293 open: 61, 71 quo: 17
mapWidget 139 open:oflag: 64, 66, 71 radiansToDegrees 17

532 IBM Smalltalk: Programmers Reference


methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued)
raisedTo: 18 right: 22 stCTime 75
raisedToInteger: 18 rightCenter 22 stDev 75
rbearing 100 rmdir: 55 stFtime 75
read:startingAt:nbyte: 66 rootDirectories 55 stGid 75
readAndDispatch 227, 229 rootWindow 81 stIno 75
readBitmapFile:widthReturn:- rounded 18, 21, 22 stMode 74
heightReturn:bitmapReturn:- roundTo: 18 stNlink 75
xHotReturn:yHotReturn: 111 sameAs: 12 stopPosition 40
readdir 59, 60 saveImage 43 storeOn: 6
readdir: 60 scaleBy: 22 storeString 6
readdirName 60, 61 screen 279 strictlyPositive 17
realizeWidget 139 searchMask: 200 stSize 74
receiver 25 searchPath: 200 stUid 75
receiver: 25 seconds 17 subclass:classInstanceVariable-
reciprocal 18 select: 11 Names:instanceVariableNames:-
recolorCursor:backgroundColor: 105 selectionLabelString: 194 classVariableNames:poolDic-
recording 132 selector 25, 42 tionaries: 39
rectangle 155 selector: 25, 42 subclass:instanceVariable-
red 113 selectors 35 Names:classVariableNames:-
red:green:blue: 113, 116 send 25 poolDictionaries: 39
redMask:greenMask:blueMask: 120 sendsSelector: 42 subclasses 34
referencesInstVar: 42 set 174 subclassResponsibility 7
referencesLiteral: 42 setAreas:verticalScrollbar: subStrings 12
rehash 11 -workRegion: 160 subStrings: 12
reject: 11 setBit: 18 subsystemType: 43, 106
release 6 setClassName: 37 subtractDate: 16
rem: 17 setClassPool: 37 subtractDays: 16
remove: 11, 56 setFont: 100 subtractTime: 17
remove:ifAbsent: 11 setPalette: 118 superclass 34
removeAll: 11 setSharedPoolNames: 37 supplantation 16
removeAllKeys: 11 setsInstVar: 42 supportsLockType: 69
removeAllKeys:ifAbsent: 11 setState:notify: 174 supportsShareMode: 70
removeAtIndex: 11 setStipple: 109 suppressing system error dialogs 73
removeClassVarName: 37 setString: 144, 162, 189 suspend 46, 47, 49
removeDependent: 6 setSubsystemType:to: 43 suspendSmalltalk 227, 228
removeFirst 11 setToEnd 24 symbol 34
removeFromSystem 38 setValuesBlock: 143 symbolLiteral 42
removeInstVarName: 37 sharedPoolNames 37 syncExecInUI: 229, 231
removeKey: 11 shouldNotImplement 7 systemBackgroundPriority 46, 50
removeKey:ifAbsent: 11 sign 18 tab 24
removeLast 11 signal 26, 27, 47, 51 tan 18
removeMappingForLogical- signal:atTime: 50 terminate 46, 49
Name: 293 signalWith: 27 textExtents:stringdirectionReturn:-
removeSharedPoolName: 37 signalWith:with: 26, 27 dRetfontAscentReturn:aRetfont-
removeUnreferencedLibraries 294 signalWithArguments: 26, 27 DescentReturn:dRetoverallReturn:-
rename:new: 56 sin 18 oRet 99
rendering 132 size 12, 23, 67, 70, 74 textLabelString: 194
replaceFrom:to:with: 12 size1 132 textWidth:string 99
replaceFrom:to:with:startingAt: 12 size2 132 time 156
replaceFrom:to:withObject: 12 skip: 24 timesRepeat: 18
reportError:resumable:startBP 47 skipTo: 24 timingPriority 46, 50
reset 24 skipToAll: 24, 64 title 159, 279
resolution 279 sleep 227 title: 198
respondsTo: 6 sortBlock 13 to: 18
resumable 25 sortBlock: 13 to:by: 18
resume 45, 46, 47, 49 sourceCodeAt: 35 to:by:do: 18
resumeWith: 27, 31 sourceCodeAt:ifAbsent: 35 to:do: 18
resumptionTime 50 sourceString 40, 42 today 16
resUnits 132 space 24 top 22
retry 27 sqrt 18 top: 22
returnType 291 squared 18 topCenter 22
reverse 10 startPosition 40 topLeft 22
reverseDo: 11 stat 74 topLeft: 22
rewind 67 stat: 60, 74, 75, 76 topRight 22
rewinddir 61 state 156 topRight: 22
right 22 stAtime 75 totalSizeBeforeUnload: 130

Index 533
methods 19, 39, 99, 486 (continued) methods 19, 39, 99, 486 (continued) OLE 437 (continued)
translateBy: 22 whichMethodsSend: 36 ambient properties 462
transpose 21 whichMethodsSendAll: 36 automation 457
trimBlanks 13 whileFalse: 25, 32 building containers 442
trimSeparators 13 whileTrue: 25, 31 classes
truncate 24 white 113 OleAutomationObject 442
truncated 18, 21, 22 whitePixel 119 OleClient 441
truncatedGrid: 21 width 22, 100, 109, 121, 155, 279 OleControl 442
truncateTo: 18, 21 width: 22 OleFile 442
type 155 width:height:depth:palette: 121 OleMainWindow 440
undefineCursor 103 window 81, 155, 164, 279 OlePrimitive 441
unload:intoByteObjects:offsetsInto- with: 10, 24 clipboard operations 449
ByteObjects: 130 with:do: 11 creating
unload:intoFile: 129, 130 with:from:to: 24 automation objects 458
unload:intoFileHandle:atOffset: 129 with:with: 10 clients 444
unloadFont 102, 133 with:with:with: 10 main windows 443
unlock:start:len: 69 with:with:with:with: 10 object verb menus 451
unmapWidget 139 withAllSubclasses 34 custom control (OCX) 460
untilMilliseconds: 50 withAllSubclassesBreadthFirstDo: 35 licensed OCXs 467
update: 6 withAllSubclassesDepthFirstDo: 35 Links dialog 454
upTo: 24, 63 withAllSubclassesDo: 35 retrieving clients 456
upToAll: 24, 63, 64 withAllSuperclasses 34 saving clients 455
upToEnd 24 withAllSuperclassesDo: 35 wrapping OCXs 466
userBackgroundPriority 46, 50 write:startingAt:nbyte: 66 OrderedCollection 8, 10, 13
userInterruptPriority 46, 50 writeBitmapFile:width:height:- origin
userSchedulingPriority 46, 50 xHot:yHot: 110 for drawing 81
value 14, 48, 111 x 21, 155, 156, 279 of graphic text 100
value: 14, 15, 48 x: 21 of Point 21
value:onReturnDo: 48 x:y: 21 OSBaseType 309, 311, 315
value:value: 48 xor: 7 OSF/Motif 135
value:value:onReturnDo: 48 xRes 132 input event processing 225
value:value:value: 48 xRoot 156 widget resource compatibility 143
value:value:value:onReturnDo: 48 y 21, 155, 156, 279 OSImmediate 309
valueOnReturnDo: 48 y: 21 OSObject 308
values 11 year 16 OSObjectPointer 309, 311
valueWithArguments: 48 yourself 7 OSStructure 309, 311, 315
valueWithArguments:onReturnDo: 48 yRes 132 OSVariableStructure 309, 315
variableByteSubclass:- yRoot 156 other languages, interfacing to 285
classInstanceVariable- modal dialog 191
Names:classVariableNames:pool- modifyVerifyCallback 163
Dictionaries: 39
variableByteSubclass:-
mouse events 154 P
pages
classVariableNames:-
callbacks 250
poolDictionaries: 39
variableByteSubclass:classInstance-
N creating 250
name and type, Volume 57 producing 278
VariableNames:classVariableNames:-
name length palettes 114, 118
poolDictionaries: 39
file 57 default 115
variableSubclass:-
volumes 58 direct 120
classInstanceVariableNames:-
National Language Support 377 indexed 116
instanceVariableNames:-
defining managed pool retrieving 118
classVariableNames:-
dictionaries 396 selecting 118
poolDictionaries: 39
overview 377 parent 135
variableSubclass:instanceVariable-
NLS 377 paths 55
Names:classVariableNames:-
notebooks 250 pattern matching 95
poolDictionaries: 39
callbacks 250 pie slices 93
version 131
PM 251 drawing 93
vmType 43
WIN 253 pixmaps
vRes 131
Number 17 as cursors 103, 105
wait 47, 48, 50, 51
copying 108
wake 227
creating 107
when:do: 26, 29
when:do:when:do:, etc. 26 O creating bitmaps 109
drawing in color 119
whenExceptionDo: 29 Object class 5, 6
freeing 107
whichClassIncludesSelector: 34 Object Linking and Embedding 437
geometry 108
whichMethodsReferenceInstVar- ObjectPointer 316
overview 106
Name: 36 OCX 460
reading files 111
whichMethodsReferenceLiteral: 36 OLE 437
writing files 110

534 IBM Smalltalk: Programmers Reference


platform differences processes (continued) Set 8, 10
Common Graphics 515 ending 46 Set values block 143
Common Widgets 517 printing 274 shell, for printer 277
platform-integrated drag and drop priorities 46 shell widgets 136, 137, 158
accepting drops 219 Process class 45, 228 decorations 158, 159
classes 217 process methods 49 Signal 25, 27
convert proc 224 resuming 46 simple
drop proc 221 suspending 46 drag and drop 270
transfer proc 225 synchronization 47 groups 262
transferring data 223 UIProcess 226 menus 179
platform-specific support ProcessorScheduler 45, 50 sliders 254
dynamic data exchange 374 progress bar tools 260 SortedCollection 8, 10, 13
PlatformConstants 106 progress bars 253 spin buttons 256
PlatformFunction prompters 197 split windows 257
calling 290 file selection 200 starting print jobs 278
declaring functions 333 message 198 stipples, creating 109
definition 285, 288 text 200 streams 61
protocols 291 push-button widgets 171, 172 String 8, 10, 13
PlatformLibrary strings, drawing 101
definition 293 suspend process, causes 47
protocols 293
PM notebook widget 251
Q Symbol 8, 10, 13
system browser font 212
queries 360
Point 20 system configuration 269
points, drawing 90 SystemExceptions 30
poke events 357
poking data to server 361 R
policies
editing 246
radio-button widgets 174
raster operations, setting 88
T
table
polygons ReadStream 22, 23
columns 243
drawing 92 ReadWriteStream 22, 23
lists 241
pool dictionaries Rectangle 21
resources 242
CfsConstants 54 rectangles, drawing 91
trees 249
CgConstants 83 renderable objects 238
target type 216
CldtConstants 15 request event 356
termination events 357
CwConstants 144 requesting data 360
text
EwConstants 233 resource management
drawing 101
PlatformConstants 106 summary 133
widgets 162
SystemExceptions 30 resources 141, 144
Time 16
pop-up common 235
timeouts 371
dialogs 158 extended widgets 202
timer proc 230
menus 183 getting 142
toggle-button widgets 171, 173, 175
windows 149, 158 setting 142
tool bars 259
POSIX locale model 379 setValuesBlock 143
groups 261
PRAGMA field 396 table list 242
primitive tools 260
primitive widgets 136 return values
simple groups 262
print job DDE transfers 363
using tools 260, 261
attributes 276 RGB colors 113
widget tools 261
configuring 275 row-column widgets
top-level shell widgets 158
configuring attributes 275 as check box 175
resources 159
ending 279 as menu 184
transfer proc 225
printers as menu bar 184
transferring data 223
selecting 274 as radio box 148, 174, 211
tree widgets 247
setting up on a UNIX platform 282 non-homogeneous error 174
icon trees 248
shell run events 357
creating 277
starting a job 278
using prompter 275 S U
printing process UIProcess 226
scrolled lists 148, 149, 188, 235
overview 274 UndefinedObject 24
scrolled-window widgets 160
using printer shells 276 UNIX
selecting printers 274
proc 217 configuring printer setup options 283
selection-box widgets 193
processes pipe stream 431
Semaphore 45, 47, 51
and delay 228 printer setup 282
separator tools 260
and Delay 47 process 427
server behavior, implementing 349
and Semaphore 47 UNIX environment 425
servers
background 228 UNIXEnvironment
executing commands at 362
creating 45 class usage example 426
poking data to 361

Index 535
UNIXEnvironment (continued)
methods 426
W widgets (continued)
parent 135
UNIXProcess 427 warmlink events 355 parent-child relationship 138
methods 428 warmlinking to items 360 pop-up menus 183
usage example 430 warnings 174 primitive 136
UNIXReadPipeStream 431 widgets progress bars 253
UNIXWritePipeStream 434 application-drawn button 177 pull-down menus 180
user interface argBlock 143 push-button 172
background request 229 button 171 radio-button 174
concepts 137 callbacks 469 realizing 139
process model 225, 231 check-box 175 resources 141, 469
user primitives child 135 row-column 169
accessing objects 323 class hierarchy 135 scrolled-window 160
classes available during 327 colors 213 selection-box 193
converting C values to Smalltalk combo-box 189 setting resources 142
objects 322 composite 136 shell 137
converting Smalltalk objects to C convenience methods 148, 150 simple menus 179
values 322 creating 138, 148 sliders 254
definition 285 creation methods 148, 150 spin buttons 256
dummy environment 328 definition 135 split windows 257
error codes 336 destroying 140 steps to display 138
external shared library 319 dialogs 190 table lists 241
functions available in 319 drawing area 164 text 162
miscellaneous functions 326 drawing in color 118 toggle-button 175
object allocation 320 drawn list 236 tool bars 259
protecting objects from garbage event handling 146 top-level shell 158
collection 326 extended list 234 tree 138
samples 328 flowsed icon lists 240 trees 247
sending messages 320 fonts used by 211 icons 248
syntax for 318 form 167 table 249
testing objects 321 functions 144 wildcard characters 95
using functions from other geometry management 161 in file names 58
places 328 getting resources 142 WIN notebook widget 253
user primitives, error codes 336 greying out button 178 window
using icon area 241 decorations 159
licensed OCXs 467 icon lists 237 manager 158
OCXs 460 iconic-button 176 work proc 230
printer shell 276 layout 167 WriteStream 22, 23
using OLE automation 457 limitations 270
list 185
main-window 161
managing 139, 141 X
V mapping 139, 140 X logical font description 95
valuemasks 85 menus 178 X11 bitmap file format 110
Virtual Machine API 285 message-box 191 Xlib
volumes notebooks 250 Common Graphics compatibility
file name lengths 58 PM 251 with 79, 112
obtaining 57 WIN 253 Common Widgets compatibility
Volume name and type 57 overview of concepts 137 with 135
voting and cursors 267 conversion to Smalltalk 79, 135
predefined cursors 104

536 IBM Smalltalk: Programmers Reference

You might also like