<<

IBM 

Programmer’s Reference

Ve s i o n 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 . 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 Magnitudecomparing...... 14 Association...... 14 About this book ...... xi ...... 14 Date...... 16 What this book includes ...... xi Time...... 16 Who this book is for ...... xi Number...... 17 About this product or feature ...... xi ...... 18 Conventions used in this book ...... xii Fraction ...... 19 References...... xii Float...... 19 Common Language Data Types, Common ScaledDecimal ...... 20 Language Implementation, and Common Graphical classes ...... 20 Model...... xii Point...... 20 Common ...... xiii Rectangle ...... 21 CommonGraphics...... xiii classes ...... 22 CommonWidgets...... xiii Accessing ...... 23 CommonPrinting...... xiv Copying ...... 23 Dynamic Data Exchange ...... xiv Enumerating ...... 23 National Language Support...... xiv Positioning ...... 24 Telluswhatyouthink...... xiv Reading...... 24 Testing ...... 24 Chapter 1. IBM Smalltalk overview . . . 1 Truncating...... 24 Base subsystems ...... 1 Writing ...... 24 Common Language Data Types ...... 1 Creatinginstances...... 24 Common Language Implementation .....1 Support classes ...... 24 Common Process Model ...... 1 UndefinedObject...... 24 Common File System ...... 1 Message and DirectedMessage ...... 25 CommonGraphics...... 2 Block...... 25 CommonWidgets...... 2 classes ...... 25 ExtendedWidgets...... 2 ExceptionalEvent ...... 25 DragandDrop...... 2 Signal ...... 25 CommonPrinting...... 2 Creatingnewexceptions...... 25 Design intent ...... 2 Signalinganexception...... 26 Architecture ...... 3 Handlingofexceptions...... 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 Basicdependentsmechanism...... 6 Chapter 3. Common Language Behavior testing ...... 6 Implementation ...... 33 Class identity testing ...... 6 Behavior messages ...... 34 Copying ...... 6 Class queries ...... 34 Printingandstoring...... 6 Compiling ...... 34 Performing ...... 7 Creatinginstances...... 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 Booleanmessages...... 7 Method queries ...... 36 Collection classes ...... 8 Classmessages...... 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 Metaclassmessages...... 38 Opening and closing file streams ...... 61 Accessing ...... 38 Reading and writing file streams ...... 62 Creating new classes ...... 38 Characters versus ...... 63 Creating fixed classes ...... 39 Line delimiters ...... 63 Creating variable classes ...... 39 Using low-level file operations ...... 64 Creating variable 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 Filelockingandsharemodes...... 68 Testing ...... 42 Filelocking...... 68 EmSystemConfiguration ...... 42 Share modes ...... 70 Handlingerrors...... 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 ObtainingaCfsStatinstance...... 74 Setting and modifying process priorities .....46 Mixing platform-specific and Common File System Synchronization using 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 CoreCommonGraphicsclasshierarchy.....80 System ...... 53 Seldom-used and abstract classes ...... 82 Basic file protocols ...... 53 A simplified drawing process overview .....83 Streamprotocols...... 53 Beforedrawing...... 83 Portability protocols ...... 53 Duringdrawing...... 83 Error handling protocols ...... 53 Afterdrawing...... 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 Usinggraphicscontexts...... 84 Portable file names ...... 54 Basic graphics context methods...... 84 File system roots ...... 55 Creatinggraphicscontexts...... 85 Pathseparators...... 55 Copying graphics contexts ...... 88 Managing files and directories ...... 55 Changing graphics contexts ...... 89 Current working directory ...... 55 Freeinggraphicscontexts...... 89 Creating and removing directories ...... 55 Using graphics contexts with other drawables . . 89 Deleting files ...... 56 Drawingoperations...... 89 Renaming files ...... 56 Drawingpoints...... 90 Copying files ...... 56 Drawinglines...... 90 Startup directory ...... 57 Drawingrectangles...... 91 Obtainingvolumeinformation...... 57 Drawing polygons ...... 92 Volumenameandtype...... 57 Drawingarcsandcircles...... 92 File names and directory path case .....57 Drawing pie slices and chords using filled arcs 93 Filenamelength...... 57 Usingfonts...... 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: Programmer’s 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 Widgetresourcesandfunctions...... 141 Usingcursors...... 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 Glyphcursors...... 104 Example of using an event handler and a Pixmapcursors...... 105 callback...... 146 Changing the color of a cursor ...... 105 Creatingandusingwidgets...... 148 Platformcursors...... 106 Widget creation convenience methods ....148 Usingpixmaps...... 106 Callbacks...... 150 CreatingapixmapusingcreatePixmap:....107 Event handlers ...... 154 Creating a pixmap using Shellwidgets...... 158 createPixmapFromBitmapData: ...... 107 Top-level widgets ...... 158 Copying pixmaps to and from windows . . . 108 Scrolled-window widgets ...... 160 Getting pixmap geometry ...... 108 Main-window widgets ...... 161 Creatingabitmapfrombitmapdata.....109 Main windows and geometry management . . 161 Creatingstipplesusingbitmaps...... 109 Textwidgets...... 162 Writing bitmaps to files ...... 110 Drawingareawidgets...... 164 Reading bitmaps from files ...... 111 Adding an event handler to a drawing area . . 166 Displayingbitmaps...... 111 Layout widgets...... 167 Common Graphics image support ...... 112 Formwidgets...... 167 Specifying colors ...... 113 Row-columnwidgets...... 169 Specifying colors as RGB intensities .....113 Buttonandlabelwidgets...... 171 Specifying colors by name ...... 113 Staticlabelwidgets...... 172 Parsing a color-specification string .....114 Push-buttonwidgets...... 172 Using palettes ...... 114 Toggle-button widgets ...... 173 The default palette ...... 115 Radio-button groups ...... 174 Creating indexed palettes ...... 116 Check boxes ...... 175 Frompixelvaluestocolorsandback....116 Icon and pixmap and button widgets . . 176 Selecting and drawing with palettes .....117 Application-drawn buttons ...... 177 Direct palettes ...... 120 Menus...... 178 Device-independent images ...... 120 Greyingoutbuttons...... 178 Creatingandmanipulatingimages.....121 Simplemenusandmenubars...... 179 Displayingimages...... 122 Creating a menu bar and pull-down menu Directcolorimages...... 122 usingsimplemenuprotocol...... 180 Copying images from a drawable .....123 Creating a secondary menu using simple menu Icons...... 124 protocol...... 181 Creatingicons...... 124 Creating a pop-up menu using simple menu Drawingicons...... 125 protocol...... 183 Loading icons from DLLs ...... 125 Non-simple menus and menu bars .....184 Using icons ...... 126 Non-simple menu example ...... 184 Image and icon file formats ...... 126 Listwidgets...... 185 Loading images from files ...... 127 Single selection lists ...... 186 Handlingerrors...... 127 Multiple selection lists ...... 187 Loading icons from files ...... 128 Scrolled lists...... 188 Unloading images into files ...... 129 Combo-boxwidgets...... 189 Unloading icons into files ...... 129 Composite-box widgets ...... 190 Unloading images and icons into ByteArrays 130 MessageBoxwidgets...... 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 Creatingandusingprompters...... 197 Resource management summary ...... 133 Messageprompter...... 198 Textprompter...... 200 Chapter 7. Common Widgets.....135 File selection prompter ...... 200 OSF/Motif compatibility ...... 135 Extendedwidgets...... 201 Common Widgets class hierarchy...... 135 Writing an extended widget ...... 202 Overview of Common Widgets user Example: a primitive extended widget ....203 concepts...... 137 Example: a composite extended widget....207

Contents v Fonts ...... 211 Leavingatarget...... 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 Simpledraganddrop...... 270 Platform-integrateddraganddrop...... 215 Widgetlimitations...... 270 Targettypes...... 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 Creatingaprintjob...... 275 Drop site widgets and CwDropSite objects . . 219 Usingtheprinterprompter...... 275 Data transfer and the CwDropTransfer object 223 Print job attributes...... 276 The process model ...... 225 Usingaprintershell...... 276 The system view ...... 226 Creatingashell...... 277 The application programmer’sview.....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 PrintingwithCommonGraphics...... 279 Creation convenience methods ...... 234 A complete example ...... 280 Extended list widgets...... 234 Setting up printers and queues on platforms 282 Commonresources...... 235 Configuring printer setup options .....283 Scrolled lists...... 235 Printerconfigurationinformation...... 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 Iconareawidget...... 241 IBM Smalltalk programming model .....286 Table list widget ...... 241 Defined types ...... 286 Table list resources ...... 242 Object types ...... 286 Tablecolumns...... 243 Macros versus functions ...... 288 Direct editing of cell values...... 245 External language interface ...... 288 Edit policies ...... 246 Parametertypes...... 288 Treewidgets...... 247 Calling a PlatformFunction ...... 290 Icontrees...... 248 Inline external calls ...... 290 Tabletrees...... 249 PlatformFunction protocols ...... 291 Notebook widgets ...... 250 PlatformLibraryprotocols...... 293 Creating pages ...... 250 Entrypoints...... 294 Callbacks...... 250 Parametertypesandreturntypes.....295 PM notebook widget ...... 251 Calling an EsEntryPoint ...... 296 WIN notebook widget ...... 253 EsEntryPoint protocols ...... 297 Progressbarwidget...... 253 Asynchronous callouts ...... 298 Sliderwidget...... 254 Calling a Platform Function asynchronously . . 298 Spinbuttonwidget...... 256 Locking resources for an asynchronous call . . 301 Split window widget ...... 257 ACOerrorsanderrorcases...... 301 Toolbarwidget...... 259 Walkbacks ...... 302 Creatingtools...... 259 ACOerrors...... 302 Usingtools...... 260 Parametertypesandreturntypes.....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 Sequenceofevents...... 264 Resourcefutureprotocols...... 307 Votingandcursors...... 267 Staticfutureprotocols...... 308 Source vote and image changes ...... 268 ACOerrorprotocols...... 308 vi IBM Smalltalk: Programmer’s Reference OSObjects ...... 308 Testcases...... 372 OSObject subclasses ...... 308 Spreadsheet...... 373 OSObject protocols ...... 310 Spreadsheet window coordinates ...... 373 OSImmediateprotocols...... 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 OSVariableStructureprotocols...... 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 ()...... 327 Overviewofinternationalization...... 379 Using user primitive functions outside user Overviewoflocalizedmessages...... 380 primitives ...... 328 Overviewofmessagecatalogs...... 382 Sample user primitives for IBM Smalltalk ....328 Locatingmessagecatalogs...... 382 Sample callback for OS/2 and 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 Platformrequirements...... 333 objects...... 384 OS/2 ...... 333 Obtaining LCMessages, LCMonetary, Microsoft , , 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 UNIXplatforms...... 335 Locale change notification and the image Solarisplatforms...... 336 startupsequence...... 388 Primitive error codes ...... 336 Tools for developing international software . . . 389 Loading externalization tools ...... 389 Chapter 12. Dynamic data exchange 339 Usingmessagetemplates...... 389 Introduction ...... 339 Referencing and initializing localized messages 391 DDE concepts and definitions ...... 339 Overview of the message localization process 392 DDE classes ...... 342 Usingmessagecatalogs...... 393 DdeClient class...... 342 Using external message dictionaries .....394 DdeServerclass...... 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 Returnvalues...... 363 LCTime...... 414 DDEprotocols...... 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 ...... 418 DdeServer public instance methods .....368 AIX,HP-UX,andSolaris...... 420 DdeClient public instance methods .....369 Protocols common to DdeServer and DdeClient 371 Chapter 14. Inter-process Converting Smalltalk objects to a ByteArray and communication ...... 425 back...... 372

Contents vii UNIXenvironment...... 425 Getting and setting properties ...... 459 Method specification ...... 425 Releasing an OLE automation object.....460 Usageexample...... 426 Using OLE custom controls (OCXs) ...... 460 UNIX process ...... 427 Creating the OLE main window ...... 461 Method specification ...... 428 Creating OCXs ...... 461 Usageexample...... 430 Implementing an ambient-property handler . . 462 UNIXpipestream...... 431 Using automation aspects of an OCX ....463 UNIX read pipe stream method specification 431 Implementing event handlers ...... 464 UNIX 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 BuildinganOLEdocumentcontainer.....442 Creating an OLE main window ...... 443 Appendix . Common graphics Creating OLE clients ...... 444 platform differences ...... 515 Enabling clipboard operations ...... 449 Creating an object verb menu ...... 451 Appendix E. Common widgets Invoking the Links dialog ...... 454 Saving and retrieving an OLE client .....455 platform differences ...... 517 UsingOLEautomation...... 457 Creating OLE Automation Objects .....458 Index ...... 525 Invoking methods of OLE automation objects 458

viii IBM Smalltalk: Programmer’s 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: Programmer’s 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 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 part’s class and select OK. Italics Special emphasis Do not save the image. Titles of publications Refer to the VisualAge Smalltalk User’s Guide. Text that the product displays The status area displays Category: Data Entry. VisualAge programming objects, such Connect the window’s 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: Programmer’s 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 Programmer’s Guide, Benjamin/Cummings, Redwood City, Calif., 1991 Common Graphics James L. Conger, Windows API Bible: The Definitive Programmer’s 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,O’Reilly & Associates, Inc., Sebastopol, Calif., 1988

Nye ed., Xlib Reference Manual, Volume Two,O’Reilly & Associates, Inc., Sebastopol, Calif., 1988

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

Steve Rimmer, -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 Publisher’s 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 System’s 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 O’Reilly, X Toolkit Intrinsics Programming Manual — OSF/Motif Eidtion, Volume Four, Sebastopol, Calif., O’Reilly & Associates, Inc., 1990

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

About this book xiii Open Software Foundation, OSF/Motif Programmer’s Reference — Revision 1.1, Englewood Cliffs, N.J., Prentice Hall, 1990–1991

Open Software Foundation, OSF/Motif Programmer’s Guide— Revision 1.1, Englewood Cliffs, N.J., Prentice Hall, 1990–1991 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 Programmer’s Guide: Writing Portable UNIX Programs, Sebastopol, Calif., O’Reilly & Associates, Inc., 1991

National Language Design Guide, Designing Enabled Products, Volume 1, 2nd ed., IBM reference SE09–8001–01

National Language Support Reference Manual, Volume 2, 2nd Ed., IBM reference SE09–8001–01

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: Programmer’s 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: Programmer’s 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 corporation’s 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: Programmer’s Reference Chapter 2. Common Language Data Types

The Common Language Data Types subsystem (CLDT) provides a platform-independent interface and implementation for the 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”.Itis convenient to include a description of Object behavior in this section, even though Object itself is not a CLDT 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 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 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: Programmer’s 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 User’s 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 . 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 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,ordo 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: Programmer’s 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.Asort 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 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 protocol—so 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: Programmer’s 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: Programmer’s 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.Itisnot 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 value—the format and graphical appearance of a character are defined by the currently selected character set. All vendors agree that the characters with values from 0–127 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: Programmer’s 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: Programmer’s 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, , 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 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:,orsetBit.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:,orsetBit:. Enumerating timesRepeat: Testing even, odd

18 IBM Smalltalk: Programmer’s 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: Programmer’s 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 , 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 stream’s 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,orArray.AReadStream can stream over the same set of classes plus Interval, OrderedCollection, SortedCollection,orSymbol.

Note: One significant change has been made to Blue Book streams—each 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: Programmer’s 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- XXXXXXXX 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: Programmer’s 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:.ADirectedMessage 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 “”). parent The exception’s 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 parent’s 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: Programmer’s 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 exception’s 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: Programmer’s 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 handler’s 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: Programmer’s 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: Programmer’s 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 .

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: Programmer’s 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: Programmer’s 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 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 metaclass’s 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: Programmer’s 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,orasPoolKey.

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: Programmer’s 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 object’s class, and that instance variable references is bound to the values of the object’s 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: Programmer’s 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’.Ifany 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 system’s 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: Programmer’s 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 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 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: Programmer’s 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 Method’s 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 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: Programmer’s 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 receiver’s priority. priority: Sets the receiver’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 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: Programmer’s 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" |ddde|

"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: Programmer’s 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: Programmer’s 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,orByteArray 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 system’s 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 platform’s 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: Programmer’s 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: Programmer’s 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 method’s 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: Programmer’s 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: |fdlen| (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: Programmer’s 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: Programmer’s 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 receiver’s 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 instance’s 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 file’s 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: Programmer’s 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 file’s 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: Programmer’s 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: Programmer’s 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 System’s 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 system’s 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: Programmer’s 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 upon—either 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 tile pattern allowing you to pixels square If GCTileStipYOrigin equals position the pattern evenly -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: Programmer’s 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 CgDrawable—either 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: Programmer’s 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,orNonconvex. 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 polygon’s 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 o’clock 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: Programmer’s 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 font’s 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 Your Application Handle to 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: Programmer’s 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 pixel size x and y foundry weight name 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: Programmer’s 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 font’s 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: Programmer’s 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 font’s per-char information. perCharNumColumns Returns the number of columns in the font’s 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

t

n

t

e

r ’A

n

r ’j’

c

ascent

e

s

c

t fo

a

t fo

s

c

c

a

CgFontStruct

tru tru baseline

t

rS

rS

a n

a

0

h

e

h

c

C

C

s

t =

g

character g

e

n

C

d C e origins

c

s

e

descent

d

CgFontStruct

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 CgCharStruct’s 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 character’s height, and the y-coordinate is the character’s 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 character’s 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: Programmer’s 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:,ordrawText: 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 string’s 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:,orfont:,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: Programmer’s 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,orCgPixmap 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:.Inthe 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: Programmer’s 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 cursor’s 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 cursor’s 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 platform’s 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 platform’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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. Xlib’s 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 image’s 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: Programmer’s 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 0–65535 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 color’s 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: Programmer’s 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 8gray9red 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 application’s 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: Programmer’s 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 application’s shell widget. This ensures that the palette’s 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 window’s 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: Programmer’s 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 drawable’s 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 receiver’s 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//15green: 0 blue: 0. colors at:i+1put: 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*20y:0width: 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 pixel’s 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 (0–255) each for red, green, and blue, whereas CgRGBColors use 16 bits (0–65535). 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: Programmer’s 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+1put: 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+1put: 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 image’s 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 image’s 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,aCgIndexedPalette creation convenience method, colorCube:, can be

122 IBM Smalltalk: Programmer’s 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 gcrgb| 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 image’s 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@0extent: 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 isa1inthemask image, the icon is opaque and the corresponding pixel in the shape image is displayed. Wherever there isa0inthe 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: Programmer’s 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 system’s 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: Programmer’s 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: Programmer’s 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@0extent: 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@0extent: 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: Programmer’s 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 receiver’s 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 image’s 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: Programmer’s 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: When to free: Images do not need to be explicitly freed. CgDirectPalette Obtained using: CgDirectPalette class>> redMask:greenMask:blueMask: Freed using: 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: 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: CgFontProp, CgGCValues, Freed using: CgRGBColor, CgSegment, When to free: These objects are informational data structures CgTextItem only, and need not be explicitly freed.

134 IBM Smalltalk: Programmer’s 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 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 widget’s 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: Programmer’s 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 user’s 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: Programmer’s Reference v Widget names are used as part of a widget’s 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-column’s 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: Programmer’s 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 1CwLabel 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 widget’s resources. All methods that are not resource methods are widget function methods. A method’s 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 widget’s resources.

Resources defined by a widget’s 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 widget’s 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: Programmer’s 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 widget’s 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 widget’s 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 widget’s 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: Programmer’s 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 user’s 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: Programmer’s 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 widget’s 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: Programmer’s 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.Acallback 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 user’s interaction with the widgets.

150 IBM Smalltalk: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 platform’s 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.ACwDialogShell 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 window’s width and height are explicitly set.

158 IBM Smalltalk: Programmer’s 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 application’s 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 shell’s 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: Programmer’s 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 application’s menu bar and the widgets that define the application’s 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 window’s menu bar, and drawingArea as the main window’s 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 widget’s 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 widget’s 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: Programmer’s 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 CwText’s 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 widget’s 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: Programmer’s 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 pointsxy|

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/ndo:[: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: Programmer’s 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 child’s 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 widget’s 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: Programmer’s 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: Programmer’s 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 Widget Label marginBottom height marginHeight

marginLeft

marginRight

marginWidth marginWidth The marginTop, marginBottom, marginRight, and marginLeft resources are typically controlled by subclasses of CwLabel or by the label’s 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 platform’s look and feel. By default, the name given in a label or button creation message is used as the widget’s 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,orShiftMask. 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']. label := shell This is a label. 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: Programmer’s 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: Programmer’s 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-column’s 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." #(0000255019225532242557 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 02527025430254302541 025410252002520000 02520025410254102541 0254102520000000).

"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: Programmer’s 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:.Ifthe 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 CwList’s 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.AsingleSelection 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: Programmer’s 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 list’s 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 | items := #('Item 1' 'Item 2' 'Item 3' 'Item 4'). Item 3 Item 1 shell := CwTopLevelShell Item 2 createApplicationShell: 'shell' argBlock: [:w | Item 3 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: Programmer’s 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 dialog’s 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 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: Programmer’s 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 dialog’s 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: Programmer’s 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: Programmer’s 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 platform’s 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 platform’s 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 for: aCwWidget v Without an associated parent widget tree, using an expression such as 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 receiver’s 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 receiver’s 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: Programmer’s 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 user’s 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: 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: Programmer’s 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 widget’s 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 widget’s 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: Programmer’s 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 callback—for example, XmNactivateCallback for an activate callback—is a pool variable that must be equal to the get method’s selector—for 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 widget’s 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 widget’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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:px y:py 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: Programmer’s 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,ornil 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 drag—CwDragContext 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: Programmer’s Reference source application must provide the dragged data in the format specified by the target type in the proc’s 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 proc’s 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 proc’s 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: Programmer’s 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: Programmer’s 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 transfer—the 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 transfer—CwDropTransfer 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 dropProc’s 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 proc’s 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 dragContext’s export targets when the drag starts. Thus, applications should not count on the order of convertProc and transferProc calls.

224 IBM Smalltalk: Programmer’s 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 proc’s 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 callData’s transferClientData.

Note: You can use the clientData parameter to find the dropProc’s 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: Programmer’s 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 programmer’s 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: Programmer’s 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 directly—it 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,toa 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: Programmer’s 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 process—otherwise 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: Programmer’s 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 widget’s 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: Programmer’s 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 list’s parent (that is, to the CwScrolledWindow). Any other form children attaching to the scrolled list should be attached to the list’s 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: Programmer’s 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: Programmer’s 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 item’s 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 item’s 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: Programmer’s 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 item’s 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 list’s 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: Programmer’s Reference appropriate for the widget’s 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 class’s 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: Programmer’s 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: Programmer’s 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 item’s 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: Programmer’s 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 receiver’s 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 notebook’s pageChangeCallback and a page’s 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 page’s 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: Programmer’s 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 notebook’s 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 notebook’s 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: Programmer’s 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 widget’s 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 widget’s 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 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: Programmer’s 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 detent’s 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 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: Programmer’s 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 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 new splitWindowExample, you get the following:

258 IBM Smalltalk: Programmer’s 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: Programmer’s 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 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 tool’s 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 tool—by 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 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: Programmer’s 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 adapter’s 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 adapter’s 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: Programmer’s 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 item’s 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: Programmer’s 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 application’s 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: Programmer’s 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 application’s 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 widget’s application sets the doit flag to false in the dropCallback’s 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,orButton3, 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: Programmer’s 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: Programmer’s 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 application’s 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: Programmer’s 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:;toCwAppContext 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: Programmer’s 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 shell’s 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 shell’s 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 shell’s 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 shell’s 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: Programmer’s 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 shell’s 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: Programmer’s 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: Programmer’s 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 user’s home directory in the file .Xpdefaults. If a user’s 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: Programmer’s 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: Programmer’s Reference Immediate objects An immediate object’s 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 object’s 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 ). 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 function’s 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: Programmer’s 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,ornil (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 . 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: Programmer’s Reference messagePattern ...fail code...

For example: send: msg to: id |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 |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 receiver’s 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: Programmer’s 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 library’s 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.Ifthe 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: Programmer’s 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: Programmer’s 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 receiver’s 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 receiver’s 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: Programmer’s 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: Programmer’s 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)tobe 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 (: .

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: Programmer’s 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 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 receiver’s value to an integer. indirectionLevel Answer the receiver’s 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: Programmer’s Reference value Answer the receiver’s 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 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,anInteger,aCharacter,oranOSObject. aValue is converted using the same rules as the pointer call-out conversion. There is one exception—instances 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,anInteger,aCharacter,oranOSObject. aValue is converted using the same rules as the pointer call-out conversion. There is one exception—instances 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,anInteger,aCharacter,oranOSObject. aValue is converted using the same rules as the pointer call-out conversion. There is one exception—instances 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: Programmer’s 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,anInteger,aCharacter,oranOSObject. aValue is converted using the same rules as the pointer call-out conversion. There is one exception—instances 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 receiver’s 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 receiver’s 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 receiver’s 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: Programmer’s 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 receiver’s 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 receiver’s 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: Programmer’s 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 receiver’s indirection level is 2, the value must be an instance of the receiver’s itemType with indirection level 1. If the receiver’s 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 receiver’s indirection level is 2, the value must be an instance of the receiver’s itemType with indirection level 1. If the receiver’s 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 receiver’s 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 ...fail code... messagePattern ...fail code... messagePattern ...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 |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 |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: Programmer’s 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 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: Programmer’s 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 toaCdouble 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: Programmer’s 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: Programmer’s 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: Programmer’s 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,orEsPrimArgument(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: Programmer’s Reference #include "esuser.h" #include

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 |self primitiveFailed

Chapter 11. IBM Smalltalk Virtual Machine API 329 makeAString |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 #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: Programmer’s Reference /* Includes different header file and redefines some result values for OS/2. */ #define INCL_WININPUT #include #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; } /*0to2|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: Programmer’s 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." |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 |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 |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: Programmer’s 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,orstruct 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,orobject value is returned from a pascal16 or cdecl16 function, it is automatically converted from 16:16 to 0:32. Immediate object values and the (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 couldbestoredintoit 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: Programmer’s 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 areinuse 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: Programmer’s 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 application’s 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: Programmer’s 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 item’s 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: Programmer’s 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 DdeServerManager’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s Reference Default database To offload DDE server event processing from the developer’s 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 DdeServerManager’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 client’s 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: Programmer’s 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: Programmer’s 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 Ifthedataisinthe 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,ornil.

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 receiver’s callback lists.

364 IBM Smalltalk: Programmer’s 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 manager’s 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: Programmer’s 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 receiver’s 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 instance’s 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: Programmer’s 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 application’s 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: Programmer’s 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: Programmer’s 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 windows—one 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: Programmer’s 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: Programmer’s Reference Chapter 13. National Language Support

National Language Support (NLS) can be very important. Today’s 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 value—its 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 user’s 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: Programmer’s 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 user’s 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: Programmer’s 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 developer’s 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: Programmer’s 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 ‘.’ 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 ‘.’

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: Programmer’s 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 strings—that 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: Programmer’s 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 21 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 ‘30’ 123,456,789 ‘32’ 1234,56,789 ‘3 2 127’ 1234,56,789 ‘320’ 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: Programmer’s 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: Programmer’s 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 state—localized 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: Programmer’s Reference Table 51. MyNlsMessage—localized for #(’french’’france’) ’MsgCopy’’Copier’ ’MsgCut’’Découper’ ’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 User’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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-') | #('*' '*' '*') | ) | )

| 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: Programmer’s 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-ibm–850 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: Programmer’s Reference Limitations NlsMessageCatalogs can contain only objects that can be dumped by the Swapper without special dumping options. (Refer to the IBM Smalltalk User’s 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 failed—working 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: Programmer’s 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

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: Programmer’s 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 receiver’s 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: #( "Triple for which messages are actually stored." "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: Programmer’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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: Programmer’s Reference supportsLanguage:territory:characterSet: Determines whether the specified file contains localized messages for the locale specified by the string’s 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 receiver’s 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 receiver’s 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: Programmer’s 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 receiver’s two-character ISO country abbreviation string. language: Sets the descriptive language string for the receiver’s locale. territory: Sets the descriptive territory string for the receiver’s locale.

414 IBM Smalltalk: Programmer’s 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 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 ’30’ monThousandsSep Set to operating system thousands separator negativeSign ’-’ positiveSign ’’ LCNumeric decimalPoint Set to same operating system-supplied value as monDecimalPoint grouping ’30’ 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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: Programmer’s 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 user’s 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: Programmer’s 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 system’s 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: Programmer’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s stderr pipe, or nil if the pipe is undefined. stdin Answers a UNIXWritePipeStream representing the receiver’s stdin pipe, or nil if the pipe is undefined. stdout Answers a UNIXReadPipeStream representing the receiver’s 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: Programmer’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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: Programmer’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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: Programmer’s 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 receiver’s 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 receiver’s pipe. Answers the receiver. nextPutAll: Writes the specified sequential collection to the receiver’s 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 receiver’s 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 receiver’s pipe. tab Writes a tab character to the receiver’s pipe.

Chapter 14. Inter-process communication 435 436 IBM Smalltalk: Programmer’s 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 Microsoft’s 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 server’s 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: Programmer’s 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 object’s 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 container’s 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 container’s 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 application’s 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 user’s 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 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 container’s 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: Programmer’s 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 OleMainWindow—first using the menus specified by the container, and then querying the server for its menu groups. In addition, real estate in the container’s 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 container’s user interface to contain an embedded or linked OLE object—the 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 container’s 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 container’s 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 and process events generated by the contained OCX v Provide hooks that allow the OCX access to specific properties of the application’s user interface components, for example, access to an application’s 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: Programmer’s 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 object’s 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 server’s Close or Exit menu items. For example, given the hostName above, the label of an activated OLE server’s 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 container’s 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: Programmer’s 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 object’s 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 object’s 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 server’s 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 object’s 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: Programmer’s 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:.IfXmANY 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: Programmer’s 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 shell’s 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 shell’s 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: Programmer’s 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 object’s 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: Programmer’s 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 widget’s 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: Programmer’s 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 object’s 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 object’s 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 object’s 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 widget’s 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: Programmer’s 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’,soan 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 object’s 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: Programmer’s 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: Programmer’s Reference An OCX container: v Provides in its user interface the site for the OCX’s 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 OCXÆs 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 OCX’s 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 container’s 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 container’s 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: Programmer’s 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 OCX’s 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: Programmer’s 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 OCX’s mouse-down event passes four parameters to its container’s 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 widget’s 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 XY

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: Programmer’s 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: Programmer’s 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 BulletinBoard’s 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 BulletinBoard’s 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 BulletinBoard’s 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: Programmer’s 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: Programmer’s 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 Frame’s shadow drawing. marginWidth: CSG 0 Specifies the padding space on the left and right sides between the child of Frame and the Frame’s 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: Programmer’s 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 widget’s 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 thelabelisdrawn. 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: Programmer’s 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 RowColumn’s children. entryCallback: C OrderedCollection new Disables the activation callbacks for all ToggleButton, PushButton, and CascadeButton widgets contained within the RowColumn widget.

478 IBM Smalltalk: Programmer’s 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: Programmer’s 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 slider’s maximum value. Set maximum before minimum to avoid warning on x. minimum: CSG 0 Specifies the slider’s 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 Scale’s slider is to have traversal on. value: CSG 0 Specifies the slider’s 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 slider’s maximum value. Set maximum before minimum to avoid warning on x. minimum: CSG 0 Specifies the slider’s 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 slider’s position between minimum and maximum.

482 IBM Smalltalk: Programmer’s 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 user’s 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: Programmer’s 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: Programmer’s 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 onceitisrealized. 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 widget’s window on all four sides. bottomAttachment: CSG XmATTACHNONE Specifies attachment of the bottom side of the child. This resource is only valid if the widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 child’s 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 widget’sparentisa CwForm.

CallData type is CwAnyCallbackData. rightAttachment: CSG XmATTACHNONE Specifies attachment of the right side of the child. This resource is only valid if the widget’s 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 widget’s 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 widget’s 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 widget’s parent is a CwForm. sensitive: CSG true Determines whether a widget will react to input events.

488 IBM Smalltalk: Programmer’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s window in pixels, not including the border area. x: CSG 0 Specifies the x-coordinate of the widget’s 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 widget’s 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 application’s 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 application’s icon mask. iconPixmap: CSG XmUNSPECIFIEDPIXMAP Specifies a bitmap that could be used by the window manager as the application’s 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 widget’s 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: Programmer’s 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 widget’s 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 widget’s 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 widget’s 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 widget’s 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 item’s 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 item’s 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: Programmer’s Reference Table 94. EwContainerList resource and callback methods (continued) visualInfoCallback: C OrderedCollection new Specifies the list of callbacks that is called when an item’s 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 item’s 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: Programmer’s Reference Table 97. EwIconArea resource and callback methods (continued) drawBackground- Callback: C OrderedCollection new Specifies the list of callbacks that is called when an item’s 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 item’s icon and its label. items: CSG OrderedCollection new A collection that represents the itemsin the list. labelOrientation: CSG XmRIGHT Specifies the orientation of each item’s 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 item’s 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 item’s 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 item’s 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 item’s 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: Programmer’s Reference Table 99. EwIconTree resource and callback methods childrenCallback: C OrderedCollection new Specifies the list of callbacks that is called when an item’s 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 widget’s 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: Programmer’s 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 widget’s 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: Programmer’s Reference Table 103. EwPage resource and callback methods (continued) resizePolicy: CSG XmRESIZEANY Controls the policy for resizing the page’s 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 page’s tab. tabLabel: CSG nil Specifies the renderable object to draw in the page’s 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 page’s 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 widget’s 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 notebook’s major tabs in pixels. majorTabWidth: CSG 50 Specifies the width of the notebook’s major tabs in pixels. minorTabHeight: CSG 25 Specifies the height of the notebook’s minor tabs in pixels. minorTabWidth: CSG 50 Specifies the width of the notebook’s 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 notebook’s 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 notebook’s 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: Programmer’s 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: slider’s 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 slider’s 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 slider’s 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 slider’s 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 widget’sarea.

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 slider’s 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 slider’s 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 slider’s 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: Programmer’s 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 slider’s 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 widget’sarea. 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 button’s maximum value for spin buttons whith the itemType resource set to XmSBNUMERIC. minimum: CSG 0 Specifies the spin button’s 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: Programmer’s 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 item’s 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 table’s visualStyle is XmETCHCELLS, then this resource determines whether all cells in this column are to be drawn as etched. Any other value for the table’s 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: Programmer’s 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 item’s 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: Programmer’s 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: Programmer’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s 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 receiver’s widget are dragged away from the receiver’s 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 receiver’s 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 receiver’s 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: Programmer’s 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),#(248),#(12848),#(12 1),#(44),#(88),#(12848), 44444) #(12 8), #(4 4 4 20), #(12 4 4 444) 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: Programmer’s 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 canonlybe0or1. canonlybe0or1. 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: Programmer’s 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: Programmer’s 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)isalways getInsertionPosition)isalways 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: Programmer’s 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: Programmer’s Reference Index

callbacks 146 (continued) CGIndexedPalette A extended widgets 203 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 freeing 102 CldtConstants 15 C loading 98 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: Programmer’s 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 copying 56 E CwToggleButton 136, 171, 211, 486 creating 55 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 double-click D EsWindowSystemStartUp 226 drawing area widget 166 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 fonts 93 H creating 25 as cursors 103 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 icon lists 237 freeing 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 loading from ByteArray 130 opening 61 G 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: Programmer’s 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 arcTan 18 fractionPart 20 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 asUppercase 12, 15 address 291 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: Programmer’s 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: Programmer’s 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 mouse events 154 P variableByteSubclass:- pages classVariableNames:- callbacks 250 poolDictionaries: 39 N creating 250 variableByteSubclass:classInstance- 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 creating bitmaps 109 when:do:when:do:, etc. 26 O 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: Programmer’s 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 Q Symbol 8, 10, 13 PM notebook widget 251 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 radio-button widgets 174 T editing 246 raster operations, setting 88 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) widgets (continued) methods 426 W 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: Programmer’s Reference