Pen Point_Application_Writing_Guide_Mar92 Point Application Writing Guide Mar92
PenPoint_Application_Writing_Guide_Mar92 PenPoint_Application_Writing_Guide_Mar92
User Manual: PenPoint_Application_Writing_Guide_Mar92
Open the PDF directly: View PDF .
Page Count: 310
Download | |
Open PDF In Browser | View PDF |
GO Technical Library PenPomt PenPofnt" PenPoint™ Application Writing Guide • GO CORPORATION GO TECHNICAL LIBRARY PenPoint Application Writing Guide provides a tutorial on writing PenPoint applications, including many coding samples. This is the first book you should read as a beginning PenPoint applications developer. PenPoint Architectural Reference Volume I presents the concepts of the fundamental PenPoint classes. Read this book when you need to understand the fundamental PenPoint subsystems, such as the class manager, application framework, windows and graphics, and so on. PenPoint Architectural Reference Volume II presents the concepts of the supplemental PenPoint classes. You should read this book when you need to understand the supplemental PenPoint subsystems, such as the text subsystem, the file system, connectivity, and so on. PenPoint API Reference Volume I provides a complete reference to the fundamental PenPoint classes, messages, and data structures. PenPoint API Reference Volume II provides a complete reference to the supplemental PenPoint classes, messages, and data structures. PenPoint User Interface Design Reference describes the elements of the PehPoint Notebook User Interface, sets standards for using those elements, and describes how PenPoint uses the elements. Read this book before designing your application's user interface. PenPoint Development Tools describes the environment for developing, debugging, and testing PenPoint applications. You need this book when you start to implement and test your first PenPoint application. PenPoinf Application Writing Guide M • • ~ GO CORPORATION GO TECHNICAL LIBRARY Addison-Wesley Publishing Company Reading, Massachusetts + Menlo Park, California + New York Don Mills, Ontario + Wokingham, England + Amsterdam Bonn + Sydney + Singapore + Tokyo + Madrid + Sanjuan Paris + Seoul + Milan + Mexico City + Taipei Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book and Addison-Wesley was aware of a trademark claim, the designations have been printed in initial capital letters. The authors and publishers have taken care in preparation of this book, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection with or arising out of the use ofthe information or programs contained herein. Copyright ©1991-92 GO Corporation. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted; in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the publisher. Printed in the United States of America. Published simultaneously in Canada. The following are trademarks of GO Corporation: GO, the PenPoint logo, the GO logo, ImagePoint, PenPoint, Grafpaper, TableServer, BaseStation, EDA, MiniNote, MiniText, and DrawingPaper. Words are checked against the 77,000 word Proximity/Merriam-Webster Linguibase, ©1983 Merriam Webster. © 1983. All rights reserved, Proximity Technology, Inc. The spelling portion of this product is based onspelling and thesaurus technology from Franklin Electronic publishers. All other products or services mentioned in this document are identified by the trademarks or service marks of their respective companies or organizations. Warranty Disclaimer GO CORPORATION MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT and Limitation of Liability LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FORA PARTICULAR PURPOSE AND NON INFRINGEMENT, REGARDING PEN POINT SOFTWARE OR ANYTHING ELSE. ,GO Corporation does not warrant, guarantee, or make any representations regarding the use or the results of the use of the PenPoint software, other products, or documentation in terms of its correctness, accuracy, reliability, currentness, or otherwise. The entire risk as to the results and performance of the PenPoint software and documentation is assumed by you. The exclusion of implied warranties is not permitted by some states. The above exclusion may not apply to you. In no event will GO Corporation, its directors, officers, employees, o~ agents be liable to you for any consequential, incidental; or indirect damages (including damages for loss of business profits, business interruption, loss of business information, cost of procurement of substitute goods or technology, and the like) arising out of the use or inability to use the documentation or defects therein even if GO Corporation has been advised of the possibility of such damages, whether under theory of contract, tort (including negligence), products liability, or otherwise. Because some states do not allow the exclusion or limitation of liability for consequential or incidental damages, the above limitations may not apply to you. GO Corporation's total liability to you from any cause whatsoever, and regardless of the form of the action (whether in contract, tort [including negligence], product liability or otherwise), will be limited to $50. U.S. Government Restricted Rights The Pen Point documentation is provided with RESTRICTED RIGHTS. Use, duplication, or disclosure by the U.S. Government is subject to restrictions as set forth in FAR 52.227-19 (Commercial Computer Software-Restricted Rights) and DFAR 252.227-7013 (c) (1) (ii) (Rights in Technical Data and Computer Software), as applicable. Manufacturer is GO Corporation, 919 Bast Hillsdale Boulevard, Suite 400, Foster City, CA 94404. ISBN 0-201-60857-X 123456789-AL-9695949392 First Printing, ' March 1992 ,. Chapter 1 / Introduction 1 ,. Chapter 3 / Application Concepts 19 Intended Audience 1.1 1 Organization of this Manual 1.2 1 PenPoint Programming is Unique 3.1 20 Other Sources of Information 1.3 2 How Applications Work Installing and Starting Applications MS-DOS Installation PenPoint Installation Installer Responsibilities 3.2 3.2.1 3.2.2 3.2.3 3.2.4 20 21 21 22 22 Running a PenPoint Application Life Cycle of a Document Activating a Document Not All Active Documents are On-Screen Application Classes and Instances 3.3 3.3.1 3.3.2 3.3.3 3.3.4 23 23 23 24 24 PenPoint Drives Your Application 3.4 24 Application Objects A Descendant of dsApp An Instance of clsWin An Instance of clsObject 3.5 3.5.1 3.5.2 3.5.3 25 26 27 28 Understanding the Application Hierarchy The Notebook's ~n Hierarchy The Desktop The Notebook Page-Level Applications Sections Floating Accessories Embedded Applications Application Data 3.6 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 28 29 33 33 33 34 34 34 35 Activating and Terminating Documents Turning a Page and msgAppClose Restoring Inactive Documents Page Turning instead of Close Saving State (No Quit) 3.7 3.7.1 3.7.2 3.7-.3 3.7.4 36 36 36 37 37 Documents, not Files and Applications No New, No Save As ... Stationery 3.8 3.8.1 3.8.2 37 37 38 Shutting Down and Terminating Applications Conserving Memory Avoiding Duplication Hot Mode Components 3.9 3.9.1 3.9.2 3.9.3 3.9.4 38 38 .,. Chapter 2 / PenPoint System Overview 3 Design Considerations 2.1 3 User Interface The Pen Notebook Metaphor 2.2 2.2.1 2.2.2 3 4 Object-Oriented Architecture 2.3 5 Architecture and Functionality 2.4 6 Kernel Layer 2.5 6 System Layer File System Resource Manager Networking. Windowing Graphics Printing User Interface Toolkit Input and Handwriting Translation Selection Manager and Data Transfer 2.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6:8 2.6.9 7 7 8 8 8 9 9 10 10 11 Component Layer 2.7 12 Application Framework Layer 2.8 12 Application Layer 2.9 13 Software Development Environment Software Development Kit Coding Conventions Extensibility 2.10 2.10.1 2.10.2 2.10.3 14 14 14 14 PenPoint Design Guidelines Conserve Memory Think Small Use a Modular Design Avoid Duplicating Data Your Application Must·R~cover Take Advantage of Object-Oriented Programming Consider Sharing Code and Data Use Document Orientation Design for File Format Compatibility Exploit the Pen Use the PenPoint User Interface 2.11 2.11.1 2.11.2 2.11.3 2.11.4 2.11.5 15 15 15 15 15 16 2.11.6 2.11.7 2.11.8 2.11.9 2.11.10 2.11.11 16 16 5 17 17 17 17 ,. .Chapter 4 / PenPoint Class Manager 39 39 40 41 Objects Instead of Functions and Data 4.1 41 Messages Instead of Function Calls 4.2 42 Classes Instead of Code Sharing Handling Messages 4.3 4.3.1 43 44 Sending a Message Message Arguments ObjectCall Parameters Returned Values How Objects Know How to Respond 4.4 4.4.1 4.4.2 4.4.3 4.4.4 45 45 46 47 47 Creating an Object Classes and Instances An Alternative Explanation The _NEW Structure Identifying _NEW Structure Elements Code to Create an Object Identifying the New Object: UIDs 4.5 4.5.1 4.5.2 48 48 48 4.5.3 4.5.4 49 51 51 52 Creating a Class New Class Message Arguments Method Tables Self Possible Responses to Messages 4.6 4.5.5 4.5.6 4.6.1 4.6.2 4.6.3 4.6.4 Chapter 5 / Developing an Application Designing Your Application Designing the User Interface Designing Classes Designing Messages Designing Message Handlers Designing Program Units Designing for Internationalization and Localization Preparing for PenPoint 2.0 Preparing for Internationalization 53 54 55 56 57 59 5.1 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.2 5.2.1 5.2.2 59 60 60 60 60 61 61 61 63 Development Strategy Application Entry Point Application Instance Data Creating Stateful Objects Displaying on Screen Creating Component Classes 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 65 65 65 66 66 Development Cycles Compiling and Linking Installing the Application Debugging 5.4 5.4.1 5.4.2 5.4.3 66 66 67 68 A Developer's Checklist Checklist Of Required Interactions Checklist of NoncEssential Items 5.5 5.5.1 5.5.2 68 68 GO's Coding Conventions Typedefs Variables Functions Defines (Macros and Constants) 5.6 5.6.1 5.6.2 5.6.3 5.6.4 70 70 64 69 70. 71 71 Class Manager Constants Exported Names 5.6.5 5.6.6 71 72 PenPoint File Structure File Header Comment Includes Defines, Types, Globals Function Prototypes Message Headers Indentation Comments Some Coding Suggestions 5.7 5.7.1 5.7.2 5.7.3 5.7.4 72 73 73 74 74 5.7.5 5.7.6 5.7.7 5.7.8 75 75 75 76 PenPoint Types and Macros Data Types Basic Constants Legibility Compiler Isolation Data Conversion/Checking Bit Manipulation Tags Return Values Return Status Debugging Function Error-Handling Macros 5.8 5.8.1 5.8.2 76 76 Debugging Assistance Printing Debugging Strings Assertions Debug Flags Suggestions 5.9 5.9.1 5.9.2 5.9.3 5.9.4 The Tutorial Programs Empty Application Hello World (Toolkit) Hello World (Custom Window) Counter Application Tic-Tac-Toe Template Application Other Code Available 5.10 5.10.1 5.10.2 5.10.3 5.10.4 5.10.5 5.10.6 5.10.7 5.8.3 5.8.4 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.8.10 77 77 77 78 79 79 79 81 82 84 84 85 85 87 88 88 89 89 89 90 90 90 Chapter 6 / A Simple Application (Empty App) 91 Files Used Not the Simplest 6.1 6.1.1 91 91 Compiling and Linking the Code Compiling Method Tables Compiling the Application Linking the Application Stamping Your Application 6.2 6.2.1 6.2.2 6.2.3 6.2.4 92 92 92 93 93 Installing and Running Empty Application 6.3 94 Interesting Things You Can Do with Empty Application 6.4 95 Code Run-Through PenPoint Source Code File Organization Empty Application's Source Code Libraries and Header Files Class UID Class Creation Documents, Accessories and Stationery 6.5 6.5.1 6.5.2 Graphics Overview System Drawing Context Coordinates in Drawing Context When to Paint 8.2 8.2.1 8.2.2 8.2.3 128 128 129 129 When to Create Things? Instance Data Is It msgNew or msgInit? Window Initialization 8.3 8.3.1 129 130 8.3.2 8.3.3 130 131 6.5.3 6.5.4 6.5.5 6.5.6 97 97 99 102 102 103 104 Where Does the Application Class Come From? Installation and Activation 6.6 6.6.1 104 104 Using Instance Data No Filing Yet 8.4 8.4.1 132 132 Handling a Message Method Table msgDestroy 6.7 6.7.1 6.7.2 107 108 Drawing in a Window 8.5 133 Possible Enhancements 8.6 133 Message Handler Parameters Parameters in EmptyAppDestroy Status Return Value Message Handlers are Private 6.8 6.8.1 8.7 133 6.8.2 6.8.3 6.8.4 109 109 110 110 111 Debugging Hello World (Custom Window) Using Debug Stream Output 6.9 111 The Debugger Stream Seeing Debug Output 6.10 111 109 6.10.1 111 Chapter 7 / Creating Obiects (Hello World: Toolkit) 113 HelloTK Compiling and Installing the Application Interesting Things You Can Do with Hello TK Code Run-Through for HELLOTKl.C Highlights of HELLOTKI Sending Messages Creating Toolkit Components Where the Window Goes Why msgAppInit? Why Did the Window Appear? Possible Enhancements 7.1 7.1.1 7.1.2 113 114 114 7.2 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.2.6 7.2.7 114 114 115 116 Highlights of the Second HelloTK Only One Client Window per Frame Layout Possible Enhancements 7.3 7.3.1 7.3.2 7.3.3 122 122 122 123 Chapter 8 / Creating A New Class (Hello World: Custom Window) Hello World (Custom Window) Compiling the Code Interesting Things You Can Do with Hello . Highlights of clsHelloWorld Highlights of clsHelloWin 8.1 8.1.1 8.1.2 8.1.3 8.1.4 120 120 121 121 125 125 125 127 127 127 Chapter 9 / Saving and Restoring Data (Counter App) 135 Saving State 9.1 135 Counter Application Compiling and Installing the Application Counter Application Highlights Counter Class Highlights Instance Data Getting and Setting Values 9.2 9.2.1 9.2.2 9.2.3 9.2.4 135 137 137 138 138 9.2.5 139 Object Filing Handling msgSave Handling msgRestore 9.3 9.3.1 9.3.2 140 CounterApp's Instance Data Memory-Mapped File Opening and Closing The File Filing the Counter Object 9.4 9.4.1 9.4.2 9.4.3 142 Menu Support Buttons 9.5 9.5.1 146 147 Chapter 1 0 / Handling Input (Tic-Tac-Toe) 141 142 143 143 145 149 Tic-Tac-Toe Objects Application Components Separate Stateful Data Objects 10.1 149 10.1.1 149 10.1.2 150 Tic-Tac-Toe Structure 10.2 Tic-Tac-Toe Window Coordinate System Advanced Repainting Strategy 10.3 151 10.3.1 152 10.3.2 152 View and Data Interaction Data Object Design Instance Data vs. Instance Info Saving a Data Object 10.4 10.4.1 10.4.2 10.4.3 151 152 152 153 153 Handling Failures During msgInit and msgRestore 10.4.4 153 The Selection and Keyboard Input How Selection Works 10.5 153 10.5.1 154 More on View and Data Interaction 10.6 155 Handwriting and Gestures Input Event Handling Gesture Handling Keyboard Handling 10.7 10.7.1 10.7.2 10.7.3 156 156 156 158 r Chapter 'I 'I / Refining the Application (Tic-Tac-Toe) 11.1 175 Glossary 275 Contributors 285 Index 287 List of Figures 3-1 Application, View, and Object Classes 3-2 The PenPoint Application Framework and 3-3 The Notebook Hierarchy as Mirrored by the File System 4-1 Message Handling by a Class and its 11.1.2 11.1.3 11.1.4 159 159 160 161 162 Installation Features 11.2 163 Stationery Creating Stationery How Tic-Tac-Toe Handles Stationery 11.3 163 11.3.1 164 11.3.2 165 Help Notebook Creating Help Documents 11.4 165 11.4.1 165 Quick Help Creating Quick Help Resources 11.5 166 11.5.1 167 Standard Message Facility Vsing StdMsg Facilities Substituting Text and Defining Buttons StdMsg and Resource Files or Lists StdMsg Customization Function 11.6 11.6.1 11.6.2 11.6.3 11.6.4 Bitmaps (Icons) Creating Icons 11.7 171 11.7.1 172 r r Appendix / Sample Code 159 Debugging .Tracing Debugf Statements and Debug Flags Dumping Objects Symbol Names 11.1.1 r r r r 168 169 170 170 171 the Notebook Hierarchy 26 30 31 3-4 The Notebook Hierarchy as Mirrored by Application Processes Ancestors 4-2 Sending msgListAddI temAt to a List 4-3 How Messages to Instances are Processed by Classes 6-1 7-1 9-1 10-1 11-1 11-2 11-3 r Empty Application Option Sheet 32 44 45 48 96 VI Toolkit Components 117 CounterApp Objects 137 Tic-Tac-Toe Classes and Instances 150 Stationery Notebook & Stationery Menu 164 Quick Help 166 Application and Document Icons 171 List of Tables 3-1 Notebook Organization and the File Chapter 12 / Releasing the Application System 173 Registering Your Classes 12.1 173 Documenting the Application Writing Manuals Screen Shots Gesture Font 12.2 12.2.1 12.2.2 12.2.3 173 173 174 174 On-Disk Structure 12.3 174 Sharing Your Classes 12.4 174 33 5-1 Generic Status Values 5-2 Status Checking Macros 82 6~1 92 WATCOM Compiler and Linker Flags 6-2 Common Header Files 10-1 Tic-Tac-Toe Files 81 102 151 Chapter 1 / Introduction .The PenPoint™ operating system is an object-oriented, multitasking operating system that is optimized for pen-based computing. Writing applications for the PenPoint operating system will present you with some new challenges. However, PenPoint contains many features that make application development far easier than development in most other environments. One feature that makes application development easier is the PenPoint Application Framework, which eliminates the need to write "boilerplate" code. In other operating systems, programmers must write code to perform housekeeping functions, such as application installation, input and output file handling, and so on. These are provided automatically by the PenPoint Application Framework. As another example, PenPoint provides most of the on-screen objects used by the PenPoint Notebook User Interface (NUl). By using these objects, your application can conform to the PenPoint NUl, without a great amount of work on your part. In this manual, you will learn about the PenPoint operating system, the PenPoint development environment, and,. of course, how to write applications for the PenPoint operating system. The PenPoint Software Development Kit (SDK) contains several sample applications that you can compile and run. These sample applications are used throughout this manual to demonstrate concepts and programming techniques. "..Intenclecl Audience 1.1 This manual is intended for programmers who want to write applications for the PenPoint operating system. It assumes that you are familiar with the C programming language and related development tools, such as make utilties. You should also be aware of the information in the companion volume, PenPoint Development Tools. Pay particular attention to Chapter 2, Roadmap to SDK Documentation, which describes the organization of the PenPoint SDK documentation and recommends a path through the manuals. ".. Organization of 'his Manual Chapter 1, this chapter, introduces the organization of this manual. . Chapter 2, System Overview, presents an overall look at the PenPoint Operating System environment. This chapter does not focus on writing applications. Chapter 3, Application Concepts, presents applications from a conceptual point of view. This chapter describes most of the parts of an application that you must 1.2 2 PENPOINT APPLICATION WRITING GUIDE write, along with parts of applications that are provided for you by the PenPoint Application Framework. Chapter 4, PenPoint Class Manager, describes the parts of the PenPoint Operating System that you use to create classes and to create instances of classes. Chapter 5, Developing an Application, discusses a number of points that you must consider when creating an application. This chapter presents a checklist that you can use to ensure that your application is complete; the chapter also and discusses GO's coding conventions. Chapter 6, A Simple Application (Empty App), introduces a minimal application. By experimenting with this application, you can see just how much the PenPoint Application Framework does for you. This chapter also describes how to compile and debug Pen Point applications. Chapter 7, Creating Objects (Hello World: Toolkit), describes how you create instances of predefined PenPoint classes and use these objects in your application. Chapter 8, Creating a New Class (Hello World: Custom Window), describes how to create custom windows and presents additional information about using instance data. Chapter 9, Saving and Restoring Data (Counter App), describes how to save and restore data from your application. Chapter 10, Handling Input (Tic-Tac-Toe), describes how to handle input, while also describing a much larger application: Tic-Tac-Toe. Chapter 11, Refining the Application (Tic-Tac-Toe), describes how to add polish to your application. Chapter 12, Releasing the Application, describes the steps necessary to make your application available to other PenPoint users. The appendix contains the complete sample code for the tutorial programs referred to in the previous chapters, along with descriptions of other sample programs provided in the SDK. Other Sources of Information For conceptual information about the various classes in PenPoint, see the PenPoint Architectural Reference. For information on running PenPoint on a PC and using the PenPoint development tools and utilities, such as the Pen Point source-level debugger, see PenPoint Development Tools,. The volume also contains a master index for all SDK volumes, except the PenPoint API Reference. For reference information on the classes, messages, macros, functions, and structures defined by PenPoint, see the PenPoint API Reference. The information in the PenPoint API Reference is derived directly from the PenPoint header files (in \PENPOINT\SDK\INC) . 1.3 Chapter.2 / PenPoint System Overview When GO Corporation undertook to build a mobile, pen-based computer system, we quickly recognized that existing standard operating systems were not adequate for the task. Those systems, designed for the very different needs of keyboard-based desktop computers, would require such extensive rewriting to support this new market that they would no longer run the installed base of applications that made them standard in the first place. We therefore determined that a new, general-purpose operating system would be needed, designed specifically for the unique requirements of pen-based computing. The result is the PenPoint operating system. This document is a brief introduction and overview of its design goals, architecture, and functionality. II'" Design Considerations 2.1 Mter extensive research and analysis, GO identified the following key requirements for pen-based system software: • A direct, natural, intuitive, and flexible graphical user interface • Strong support for handwriting recognition and gesture based commands • A richer organizational metaphor than the traditional file-system model +A high degree of memory conservation through extensive sharing of code, data, and resources • Ability to run on RAM-only as well as_disk-based computers • Priority-based, preemptive multitasking • Detachable networking and deferred data transfer • Hardware independence (ability to move to new processors quickly) The PenPoint operating system was developed to satisfy these requirements. II'" User Interface . PenPoint's most distinctive feature is its innovative user interface. The user interface is the cornerstone on which the entire system is built; all other design considerations follow from it. The user interface, in turn, is based on two main organizing principles: • The use of a pen as the primary input device • A notebook metaphor that is natural and easy to use The consequences of these two basic design features permeate the entire system. 2.2 4 PEN POINT APPLICATION WRITING GUIDE ".. The Pen 2.2.1 The pen naturally combines three distinct system control functions: pointing, data input, and command Invocation. Like a mouse, it can point anywhere on the screen to designate an operand, specify a location, draw a picture, drag an object or select from a menu. Through sophisticated handwriting recognition software, it can replace the keyboard as a source of text input. Finally, it can do something neither a mouse nor a keyboard can do: issue commands through graphical gestures. "." Gestures 2.2.1.1 Gesture$ are simple shapes or figures that the user draws directly on the screen to invoke an action or command. For example, a scratch out X gesture is used to delete, a circle 0 to edit, and a caret /\ to insert. A set of built-in core gestures form the heart of the PenPoint user interface: Check ../ Caret /\ Circle 0 Flick left Flick up I Flick down Insert space L Press 1 Tap press Cross out X Flick right ·1 Pigtail Tap I Y Undo ~ To exploit the unique properties of the pen, PenPoint provides strong support for gestural command invocation. The same handwriting translation subsystem that recognizes characters for text input also recognizes those shapes that constitute meaningful gestures. The form, location, and context of the gesture then determine the action to be performed and the data objects affected. Because a gesture can be made directly over the target object, it can specify both the operand and the operation in a single act. This gives the pen-based interface a directness and simplicity that cannot be achieved with a mouse. "." IIPenPointl l Control The pen has one more notable property as a control device. Because it draws directly on the face of the screen (rather than on a physically separate working surface such as a mouse pad or graphics tablet), it eliminates a major source of difficulty among new computer users-the relationship between movement of the and the movement of the cursor on the screen. With a pen, the user's eye is focused exactly where his or her hand is working. Most PenPoint applications can thus dispense with an on-screen cursor for tracking the pen, though it can still be offered as an optional user preference. 2.2.1.2 CHAPTER 2 I PENPOINT SYSTEM OVERVnW Object-Oriented Architecture ". Notebook Metaphor 2.2.2 Instead of a traditional file system based on a hierarchy of nested directories and cryptic file names, PenPoint uses a "notebook" metaphor for information storage and retrieval. By using familiar models of working with paper-based documents, the notebook approach provides a rich variety of natural and intuitive techniques for organizing and accessing information: • A bookshelf upon which multiple user notebooks may reside, as well as system notebooks for help information and stationery, an inbox and outbox, and various tools and accessories. A user can have any n~mber of notebooks open at once; typical use involves one main notebook. • A table of contents offering an overview of all available documents in the notebook, allowing easy manipulation and navigation at the global level. The table of contents can be organized in natural page number order, or sorted by name, size, type or date. • Sections and subsections for hierarchical organization. • Page numbers and notebook tabs for direct random access • Page turning for sequential access Because the notebook is a familiar, physical, and stable model, a user can employ spatial memory oflayollt and juxtaposition to help find and organize their information. ".Obiec'-Oriented Architecture To facilitate code sharing and overall memory conservation, PenPoint uses an object-oriented approach to system architecture. All application programming interfaces (APIs) above the kernel layer are implemented using object-oriented programming techniques of subclass inheritance and message passing. This helps to ensure that PenPoint and its APIs have these characteristics: • Compact, providing a body of shared code that need not be duplicated by all applications • Consistent, since all applications share the same implementation of common system and user interface functions • Flexible, allowing applications to modify PenPoint's behavior by sub classing its built-in classes The event-driven, object-oriented nature of the system minimizes the need to "reinvent the wheel" with each new application. Programmers can "code by exception," reusing existing code while altering or adding only the specific behavior and functionality that their own applications require. Because the object-oriented architecture is system-wide, these benefits are not restricted to single applications; in fact, applications can share code with each other just as readily as with the system itself. 2.3 5 6 PENPOINT APPLICATION WRITING GUIDE Architecture and Functionality 2.4 Pen Point' s overall software architecture is organized into five layers: 1 The kernel, which provides multitasking process support, memory management, and access to hardware. The kernel works closely with the PenPoint class manager, which makes PenPoint object oriented. 2 The system layer, which provides windowing, graphics, and user interface support in addition to common operating system services such as filing and networking 3 The component layer, which consists of general-purpose subsystems offering significant functionality that can be shared among applications 4 The Application Framework, which serves as a "head start" for building applications 5 The applications themselves Each of these layers is discussed in detail below. Kernel Layer 2.5 The kernel is the portion of the PenPoint operating system that interacts directly with the hardware. Besides handling such low-level tasks as process scheduling and synchronization, dynamic memory allocation, and resource management, it also provides these services, which .are needed to support the object-oriented software archi tecture: • Priority-based, preemptive multitasking • Processes and threads (lightweight tasks sharing the same address space) • Interprocess communication and semaphores • Task-based interrupt handling • 32-bit flat memory model • Protected memory management and code execution • Heap memory allocation with transparent relocation and compaction (no fixed-length buffers) • Object-oriented message passing and subclass inheritance All hardware dependencies in the kernel are isolated into a library subset (the MIL or machine interface layer) to facilitate porting to a wide variety of hardware and processor architectures. The kernel runs on both PC and pen-based machines. All of Pen Point's APIs use full 32-bit addresses. Other parts of the kernel layer support features that keep PenPoint small and efficient, such as: J Loader Unlike a traditional, disk-based operating system, PenPoint's loader does not require multiple copies of system and application code to be present in the machine at the same time. Instead, it maintains a single CHAPTER 2 I PEN POINT SYSTEM OVERVIEW '7 System Layer III instance of all code and resources, which are shared among all clients. When installing a new application, the loader reads in only those components that are not already present in memory. == ~ III > II Power Conservation When running on battery-powered hardware, the kernel reduces power consumption by shutting down the CPU whenever there are no tasks awaiting processor time. Subsequent events such as pen activity or clock-chip alarms generate interrupts that reactivate the CPU. The kernel also monitors the main battery and will refuse to run if power is too low, ensuring reliable protection of user data. Diskless Reboot Hardware memory protection is used to preserve the integrity of all code, resources, and data. In the event of a crash, the kernel can restart all processes (including its own), with minimal loss of user data and without the need to restart the system from disk drives. C Runtime The kernel includes runtime support for all WATCOM C/386 runtime functions except those DOS- and PC-specific calls that are not applicable to a pen-based notebook environment. Class Manager PenPoint's Class Manager works closely with the kernel to support object-oriented programming techniques such as singleinheritance subclassing and message passing. The Class Manager also provides important protection and multitasking services not found in C++ or other object-oriented languages. These services safeguard the operating system against possible corruption arising from the use of object-oriented techniques. For example, instance data for system-defined classes is protected so that the data cannot be altered by any subclasses. Applications thus derive the benefits of subclassing without jeopardizing the integrity of the system. System Layer PenPoint's system layer provides a broader range of support services than a traditional operating system. In addition to the usual system facilities such as filing and networking, it also provides such high-level services as windowing, graphics, printing, and user interface support. This helps keep application code compact and consistent while facilitating application development for the machine. File System PenPoint's file system is designed for compatibility with other existing file systems, particularly MS-DOS, and includes full support for reading and writing MS-DOS-formatted disks. It provides many of the standard features of traditional file systems, including hierarchical directories, file handles, paths, and current working directories, as well as such extended features as 32-character file names, memory-mapped files, object-oriented APIs, and general, client-specified attributes for files and directories. The PenPoint file system is a strict superset of the MS-DOS file system; all PenPoint-specific information is stored as an MS-DOS file within each MS-DOS 2.6 8 PENPOINT APPLICATION WRITING GUIDE directory. This approach is used when mapping to other file systems as well. Additional, installable volume types are also supported. At present, these include RAM volumes and remote volumes (for access to PC, Macintosh, and file servers). ". Resource Manager 2.6.2 Pen Point's Resource Manager and the resource files that it controls allow applications to separate data from code in a clean, structured way. The Resource Manager can store and retrieve both standard PenPoint objects and applicationdefined data, in either a specific file or a list of files. Resources can be created directly by the application or by compiling a separate, text-based resource definition file. ". Networking 2.6.3 Pen Point provides native support for smooth connectivity to other computers and networks. Multiple, "autoconfiguring" network protocol stacks can be installed on the fly. AppleTalk protocol is built in, enabling connection to other netWorks through a variety of AppleTalk-compatible gateways. By purchasing the appropriate TOPS software, users can configure their systems to connect directly to DOS or Macintosh computers~ Through the use of these networking facilities, remote services such as printers are as .easily accessible to PenPoint applications as if they w,ere directly connected. -Remote file systems on desktop computers and-network file servers are also transparently available via a remote-file-system volume. A user can browse PC and file-server directories, for instance, using PenPoint's connections notebook. Several remote volumes can be installed at once: for example, a PenPoint system can hook directly to a Macintosh and a DOS computer at the same time . . A typical user, while on an airplane, might mark up a FAX, fill out an expense report to be electronically mailed to the payables department, draft a business letter to be printed, and edit an existing document to be returned to its "home" on a PC's hard disk. Upon connection to the physical devices, conventional operating systems would require that user to run each application, load each document and dispense with it. Pen Point's In Box and Out Box services allow the user to defer and batch data transfer operations for completion at a later time. Upon return to the office and establishing the physical connection, the documents are automatically faxed, printed, and mailed. These services are extensible and can support a wide variety of transfer operations, including electronic mail, print jobs, fax transmissions, and file transfers. ". Windowing The window system supports nested hierarchies of windows with multiple coordinate systems, clipping, and protection. Windows are integrated with Pen Point's input system, so that incoming pen events are automatically directed to the correct process and window. Windows use little memory and can therefore be used freely by applications to construct their user interface. 2.6.4 CHAPTER 2 / PENPOINT SYSTEM OVERVIEW System Layer Usually windows appear on screen, but they can also be created on other, off-screen image devices, such as printers. Tlie window system maintains a global, screen-wide display plane called the acetate plane, which is where ink from the pen is normally "dribbled" by the pen-tracking software as the user writes on the screen. The acetate plane greatly improves the system's visual responsiveness, both in displaying and in erasing pen marks on the screen. Graphics 2.6.5 PenPoint's built-in graphics facility, the ImagePoint™ imaging model, unifies text with other graphics primitives in a single, PostScript-like imaging model. ImagePoint™ graphics can be arbitrarily scaled, rotated, and translated, and can be used for both screen display and printing. ImagePoint's graphics capabilities include these elements: Polylines Bezier curves Rectangles Ellipses Rounded rectangles Arcs Polygons Sectors Sampled images Chords Text A picture segment facility allows ImagePoint messages to be stored and played back on demand, facilitating a variety of drawing and imaging applications. For improved performance, the imaging system dynamically creates machine code when appropriate for low-level graphics operations such as direct pixel transfer ("bitblt"). The ImagePoint interface also supports the use of color, (specified in conventional RGB values) allowing PenPoint to run on grey-scale and color screens. To conserve memory, ImagePoint™ uses outline fonts to render text at any point size. (Bitmap fonts are automatically substituted at low resolutions for improved visual clarity.) Fonts are heavily compressed and some character styles are synthesized to minimize memory requirements. If a requested font is not present, ImagePoint will find the closest available match. Text characters can be scaled and rotated in the same way as other graphical entities. Printing The ImagePoint imaging model is used for printing as well as screen display, allowing applications to use the same image-rendering code for both purposes, rebinding it to either a screen window or a printer as the occasion demands. PenPoint handles all printer configuration, and automatically controls margins, headers, and footers, relieving the application of these details. (As in most other areas of PenPoint, applications can override the default behavior.) 2.6.6 9 10 PENPOINT APPLICATION WRITING GUIDE One key benefit of this approach is that documents to be faxed are rendered specifically for a 200 DPI output device. The resulting output will be of sufficiently high quality that mobile users may not require a portable printer at all, opting instead to use a nearby plain paper FAX machine. PenPoint supports dot-matrix and HP Laserjet printers. When the printer does not have a requested font, the ImagePoint imaging model will render and download one from its own set of outline fonts, ensuring good WYSIWYG correspondence and shielding the user from the complexities of font management. User Interface Toolkit 2.6.7 PenPoint's User Interface Toolkit offers a wide variety of on-screen controls: Menu bars Nonmodal alerts Pulldown menus Push buttons Section tabs Exclusive choice buttons Window frames Nonexclusive choice buttons Title bars Pop-up choice lists Scroll bars List boxes Option sheets Editable text fields Dialog boxes Handwriting pads Progress bar Grabbers Modal alerts Busy clock Trackers A major innovation in PenPoint's User Interface Toolkit is automatic layout. Instead of specifying the exact position and size of controls, the application need only supply a set of constraints on their relative positions, and the Toolkit will dynamically calculate their exact horizontal and vertical coordinates. This makes it easy for programmers or users to resize elements of the user interface, change their ' fonts or other visual characteristics, or switch between portrait and landscape screen orientations, while preserving the correct proportions and positional relationships. Input and Handwriting Translation PenPoint's input subsystem translates input events received by the hardware into messages directed to application objects. The low-level pen events include: In proximity Out of proximity Tip down Tip up Move down Move up Window enter Window exit These low-level events can be grouped into higher-level aggregates called scribbles, which are then translated by the handwriting translation (HWX) subsystem into either text characters or command gestures. These characters or 2.6.8 CHAPTER 2 / PENPOINT SYSTEM OVERVIEW System Layer gestures in turn are dispatched to the appropriate objects via a rich input distribution model that includes filtering, grabbing, inserting, and routing of input up and down thewindow hierarchy. . The portion of the GOWrite handwriting translation engine that matches and recognizes character shapes is replaceable, allowing PenPoint to improve its HWX techniques as better algorithms become available. There are two parts to the handwriting translation engine: the first part matches shapes, the second part uses context to improve the translation.. . The current HWX engine, developed entirely by GO, recognizes hand-printed characters and has the following characteristics: • .operates in real time (shape matcher operates at 60 characters per second on 33Mhz 80486) • Runs in a background process • Handles mixed upper- and lowercase letters, numerals, and punctuation • Tolerates characters that overlap or touch • Recognizes characters independently of stroke order, direction, and time order • Distinguishes non-unique character forms such as the letter "0" and the numeral "0" (in context) • Tolerates inconsistency by same user (that is, the user may shape the same character in different ways at different times) • Can accept optional context-sensitive aids (such as word lists, dictionaries, and character templates) provided by an application. Applications are given great control over this process; they may issue constraints that merely influence the result or force a match against a predefined list. Although PenPoint is designed primarily for pen-based input, it is not limited to the pen. For high volume data entry, PenPoint is designed to accept input from a keyboard. As an alternative, PenPoint also provides a software "virtual keyboard." Users can display the keyboard on the screen and input text by tapping on the keys with the pen. 'Y Selection Manager and Data Transfer The Selection Manager subsystem maintains a system-wide selection, which is the target for all editing operations. The Selection Manager also implements a single-level stack for temporarily saving the current selection. Editing is based on a move-and-copy model, rather than a "clipboard" (cut-and-paste) model. The source and destination applications negotiate data transfers from one application to another. The destination application requests a list of available data formats from the source application. PenPoint supports a variety of standard transfer formats, including RTF (Rich Text Format), structured graphics, and TIFF 11 12 PENPOINT APPLICATION WRITING GUIDE (Tagged Image File Format); applications can extend this list to include other formats as well. PenPoint's object-oriented architecture also makes possible the PenPoint EDATM or embedded document architecture. This isa unique form of "live" data transfer in which the transferred data carries with it an instance of its own source application. Through object-oriented message passing, this embedded application instance can then be used to display, edit, or otherwise manipulate the data from within the destination application. Although more conventional forms of "hot links" and DDE (Dynamic Data Exchange) linking are still possible in Pen Point, such live application embedding obviates the need for most of them. ".. Component Layer 2.7 Above and beyond the traditional kernel and system facilities, PenPoint adds a rich, powerful, and extensible component layer. Components are general-purpose code units with application-level functionality that can be shared and reused as building blocks by multiple client applications. They speed the development of applications, reduce memory consumption, and provide for more consistent user . interfaces and tighter integration across diverse applications. PenPoint includes several components, such as a multi-font, WYSIWYG text editor and a scribble editing window that can be embedded within any application that needs them. You can include these components in your application without licensing them from GO. Third-party developers may market components to other developers. Applications may also provide their own general-purpose components to be installed and shared in the PenPoint runtime environment. ".. Application Framework Layer The Application Framework is a set of protocols rigorously defining the structure and common behavior of a PenPoint application. Through the Application Framework, applications inherit a wide variety of standard behavior, including installation and configuration, creation of new documents, application stationery (template documents), on-line help, document properties, spell checking, search and replace, import/export file dialogs, and printing. New code is required only for added functionality or to modify or override specific aspects of the default behavior. Use of the Application Framework thus yields significant savings in programming time and code space. An application developer creates the application code and any resources needed by the application. When a user installs an application, the PenPoint Application Framework takes care of: • Copying the application code and all other auxiliary files to the system • Creating new documents • Creating and terminating taSks 2.8 CHAPTER 2 / PEN POINT SYSTEM OVERVIEW Application Loyer • Storing and retrieving user data in the file system • Creating and destroying a main window for the application. Active documents save their internal state in the file system, but this is invisible to the user: there is no need to save or load the application's state explicitly from one session to the next. The most innovative aspect of PenPoint's Application Framework is its ability to create true "live compound documents." Users and developers can freely embed a document created by one application inside another document (for example, a business graphics application within a text document). GO refers to this architecture as EDATM (Embedded Document Architecture). ". Application Layer Using the "live" recursive embedding available through EDA, PenPoint's ' notebook metaphor and user interface are implemented as a set of bundled system applications. Although the user simply perceives these collectively as "the notebook," they are in fact distinct applications, providing a cleanly delineated and modular architecture. The key bundled applications include bookshelf, notebook, and section applications that together constitute the core notebook metaphor. • The Table of Contents (TOC) application provides a user interface for specialized organization and retrieval at the front of the notebook. • A bundled text editor provides end users with intuitive, pen-based Rich Text editing. • A standard Send user interface and an Address List allow for the addressing of all electronic mail, fax, and file transfers. • A file browser allows the user to point'to files and directories and use standard gesture commands to manipulate them. Multiple instances of the notebook can be created; in fact, the Create, Help, Configuration, and InBox/OutBox applications are all instances of the notebook application. Developers benefit from this code sharing; users benefit from decreased memory requirements as well as greater consistency in the user interface. The Help Notebook, for example, consists of help documents ordered by section (application), and therefore looks just like the standard table of contents. Users already know how to navigate through this notebook and can even create hyperlink references to important sections. Developers can simply write ASCII text to provide on-line documentation. Documents in the help notebook can be any type of PenPoint application documents. Developers can also leverage existing application code to build very powerful help systems that can demonstrate real functionality. 2.9 13 14 PENPOINT APPLICATION WRITING GUIDE Software Development Environment 2.10 With the exception of some hardware-dependent code, PenPoint and the applications it supports are written entirely in ANSI C, using current versions of leading PC-based development tools. Developers already acquainted with object-oriented concepts, and with the graphical user interfaces and multitasking found in operating systems like Macintosh and OS/2 Presentation Manager, will find the development environment familiar and will quickly be able to do productive work. Software Development Kit 2.10.1 The PenPoint SDK provides developers with the documentation and tools to develop applications. The kit includes a source-code symbolic debugger, as well as an outline font editor for creating scalable and rotatable application-specific glyphs. Because PenPoint runs on DOS 386 machines, the full application edit-com pile-debug cycle can be accomplished solely on a Pc, or on a combination of a PC and a computer running PenPoint. In the former configuration, you use a pen-driven digitizer tablet to simulate pen input. In the latter configuration, the PC serves as a debugging monitor, as well as a convenient repository of the development system libraries, header files, on-line documentation, and source code. Coding Conventions 2.10,2 All PenPointcode is written in accordance with modern software engineering standards, including: • Consistent naming conventions for modules, functions, and variables • Carefully designed modularity • Proper commenting and formatting of source code. Almost all of the C code is structured using object-oriented programming techniques. Classes are defined and objects are created and sent messages by making calls into a library of C routines called the Class Manager. These techniques are in the mainstream of currently evolving industry practices, but the details are unique to GO and are well documented in the SDK materials. Extensibility Pen Point is extensible in a variety of ways, allowing for the addition of new networking protocols, imaging models, font models, and file-system volumes. Pen Point can run on computer architectures ranging from RAM-only, pen-based pocket computers to powerful disk-based workstations with p~n-tablet screens. The operating system is a working whole, with most modules integrated and tested as part of the full system since early 1988. Through techniques such as hardware memory protection, object-oriented programming, rigorous 0.3 CHAPTER 2 I PEN POINT SYSTEM OVERVIEW Pen Point Design Guidelines modularization, and extensive sharing of code, PenPoint has the foundations of a highly reliable operating system. PenPoinl Design Guidelines 2.11 To this point, this chapter has presented concepts that relate to the PenPoint operating system as a whole. The remainder of the chapter describes important points that application developers will have to keep in mind while designing and coding PenPoint applications. "., Conserve Memory 2.11.1 Do not squander memory. Your application should use little memory when active. It must be .able to further reduce its memory usage when off-screen. An application that is packed with functionality but consumes a lot of memory is less likely to be successful than one which meets the key needs while requiring very little memory. "., Think Small 2.11.2 Most PC programs stand alone as large monolithic programs that attempt to do everything. In the cooperative, multitasking PenPoint en~ironment with its Embedded Document Architecture, it makes more sense to provide programs that present a facet of functionality or that orchestrate other applications and components. Use existing classes and components where possible rather than writing your own from scratch. "'''' Use a Modular Design 2.11.3 Consider writing your application as a set of separable components. A component is a separately loadable module (a dynamic .link library or DLL) that provides software functionality. A component has a well-defined programmatic interface so that other software can reuse it or replace it. With modular design, your application becomes an organizing structure that ties together other components in a useful way. For example, an outliner applicat!on might use a drawing component, a charting component, and a table entry component; you could license these components to or from other developers. GO is working to develop a market for third-party components, and itself offers several components, including Text View™ and the TableServerTM. "'''' Avoid Duplicating Data In some Pen Point memory configurations (single-tier, RAM volume), everything is in memory: The RAM volume co-exists with running applications in the same memory. Usually, computers running PenPoint will not be attached to external media. You should be aware of the occasions when data in your application's memory space needlessly duplicates data or code that is also present in the file system. One way to avoid duplication is to use memory-mapped files for your application's data. 2.11.4 15 16 PENPOINT APPLICATION WRITING GUIDE When designing for a memory-resident file system, many of the trade-offs appropriate to traditional software design no longer apply. For example, deciding to read a startup file "into memory" makes sense when memory access is several orders of magnitude faster than file access, but in the case of single-tier memory configurations, the file system is memory. Your Application Must Recover Users may go for weeks or months without backing up their PenPoint computer's file system. If your application goes wrong, the PenPoint operat·ing system will try to halt your application rather than the entire computer, but it is your responsibility to ensure that a new invocation of your application will be able to recover cleanly using whatever information it finds in the file system. This precept sometimes conflicts with avoiding data duplication, because the memory file system is more bullet-proof than the address space of a running application. For this reason, filed state will usually survive a process crash. Moreover, most users will not have the PenPoint computer boot disks on hand. That means you cannot rely on the user being able to press the reset switch in a jam. PenPoint uses hardware and software protection techniques to secure against applications unintentionally corrupting the kernel and/or file system, but it is not foolproof. Take Advantage of Obiect-Oriented Programming You don't get to vote on using object-oriented techniques. You must write a class for your application that inherits from clsApp. The windows your application displays on the screen must be instances of cIsWin (or instances of a class that inherits from cIsWin). Of course, there are tremendous payoffs from PenPoint's object-oriented approach in program size reduction, code sharing, application consistency, programmer productivity, and elimination of boilerplate code (those large chunks of setup or housekeeping code that appear unchanged in every application) . Consider Sharing Code and Data Think about what other parts of Pen Point need to access your classes, what tasks need to run the code in them, and who maintains their data. If your application has a client-server architecture, a separate back-end or a core engine, you'll need to have the picture in mind when choosing local or global memory, dynamic or well-known objects, process or subtask execution, protecting shared data with semaphores and queued access, and so on. PenPoint is a rich operating system that makes its kernel features available to applications. But a straightforward application may not need to concern itself with any of these decisions. It just interacts with PenPoint subsystems, which make careful use of these features. For example, none of the tutorial programs use any advanced kernel features. .7 CHAPTER 2 I PEN POINT SYSTEM OVERVIEW Pen Point Design Guidelines ?r Use D@~lIJIme~~ OW"gelrilt~tk~Hru ~ 2.11.8 [ 2.11.9 The PenPoint application environment differs from that of other operating systems in that PenPoint saves your application data, along with information about objects in the document. Because of this filing method, your data formats within PenPoint will differ from their PC equivalents. Most PenPoint users, however, will need to read and write application data in formats that are understood by other non-PenPoint applications. Either your application should be able to read and write data in other formats, or you should create an i~port or export filter for your PenPoint files. Pen Point provides import and export filters for some common file formats. Because the· import-export mechanism is class based, you or other application developers can create import-export filters for other file formats. 2.11.10 Graphical user interfaces built around a mouse or other pointing devices lead to flexible program architectures that respond to the user's actions instead of requiring the user to perform certain steps. The pen-oriented notebook interface of PenPoint is even more free-form. Just as with a mouse, the user can point to and manipulate (click, drag, stretch, wipe) entities on-screen, but in the PenPoint operating system the user can also make gestures and handwrite characters "on" the visual entities. Taking advantage of the pen is a challenge and a tremendous opportunity. ",. Use the Pen Point User Interface The Notebook User Interface (NUl) differs from other graphical user interfaces. If you are porting a DOS or Macintosh-based program to PenPoint, rethink your user interface so that it takes advantage of the PenPoint UI Toolkit. Do not create your own interface. ~ III There are many ramifications of this orientation: applications have rio Open ... or Save As ... commands; the PenPoint operating system, not the user, saves data and quits programs; you deliver application templates and defaults to the user as stationery. ",. Exploit the Pen III > In the PenPoint operating system, the user sees documents, not separate programs and program files. Every document on a page is the conjunction of data and a process running an application. This leads to a document-centered approach to application design in place of a program-oriented approach. By comparison, on a Macintosh or IBM-PC compatible computer, the user tends to fire up a program and work on a succession of files. Under PenPoint, the user turns to a new document (or taps in a floating document) and the system unobtrusively turns control over to the right program for that document. ",. Design. for File Format Compatibility 17 2.11.11 18 PENPOINT APPLICATION WRITING GUIDE. The PenPoint UI Design Reference describes the PenPoint User Interface, its rationale, and how and when to use its components. You should have good reason before you deviate from the PenPoint interface. Remember that a consistent user interface allows users to learn your application quickly; a bad or inconsistent user interface will count against your application in product reviews (and acceptance in the marketplace). The PenPoint UI toolkit contains classes that create almost every on-screen object in the PenPoint NUL If you use these classes, it is hard to deviate from the standard. Additionally, it is easier to follow the conventions by using these classes than to subclass and change their default behavior. Chapter 3 / Application Concepts This chapter gives you the big picture of application development for the PenPoint™ operating system. It introduces the design issues you need to consider when writing an application for a mobile, pen-based computer, how applications work under Pen Point, and how you use the PenPoint classes. This chapter also presents concepts in general terms to provide the fundamental understanding that puts the balance of this manual in context. You needn't have read any of the other documentation before reading this chapter. However, if you have the SDK software, you might want to read the "Getting Started" document in the Open Me First packet for detailed instruction on how to compile and run the tutorial programs. If you want a basic look at how the PenPoint operating system works, without a focus on writing applications, read Chapter 2, System Overview. If you need an introduction to object-oriented programming, read these industry publications: • Principles ofObject Oriented Design, Grady Booch, The Benjamin/Cummins Publishing Co., 1991 • Object-Oriented Programmingfor the Macintosh, Kurt Schmucker, Hayden Book Company, 1986. • Object-Oriented Programming: An Evolutionary Approach, Second Edition, Brad]. Cox and Andrew J. Novobilski, Addison-Wesley Publishing Company, 1991. However you do it, make sure you come to understand the basics of object-oriented programming, because in PenPoint every application must be class-based. This chapter points out some of the aspects of the PenPoint Operating System that particularly affect your approach to application design. As you know, application development takes place at two levels: • At an architectural level, where you design your application • At the line-by-line level of program statements. At the architectural level, this chapter assumes that you have basic familiarity with object-oriented programming. In developing a PenPoint application you'll be designing different kinds of objects and the interactions between them and PenPoint. The section "How Applications Work" introduces the PenPoint Application Framework, which influences and supports the structure of all PenPoint applications. 20 PENPOINT APPLICATION WRITING GUIDE At the programming statement level, this chapter assumes that you are well-versed in C programming. You'll be writing C code that makes heavy use of the Pen Point Class Manager. The section "Understanding Classes" introduces the Class Manager and shows you what lines of code in PenPoint look like. With some understanding of the Application Framework and the Class Manager, you'll have the tools necessary to understand simple programs both architecturally and line-by-line. Later chapters in this manual describe the SDK sample programs in \PENPOINT\SDK\SAMPLE (the installation procedure for the SDK creates the \PENPOINT directory on your hard disk) . PenPoint Programming is Unique 3.1 Just as a PenPoint™ computer is used in work environments that differ from other computers, PenPoint applications execute in an environment that differs from conventional PC application environments. There are eight key differences: • Stylus-based user interaction • Object-oriented programming • Disk storage not necessary • Multitasking • Cooperating, simultaneously active, embedable applications • Graphics-intensive user interface • Notebook metaphor • Document-orientation instead of applications and files orientation. Dealing with these aspects of PenPoint requires you to observe a number of guidelines, described in the following sections. The good news is that the software architecture of PenPoint shoulders much of the load for you. The Class Manager supports the pervasive use of classes and objects throughout Pen Point; not only in the user interface area, but also in areas such as the file system and the imaging model. These classes provide you with ready-made components which you can use as is or customize in your applications. These objects already conserve memory, exploit the pen interface, cooperate with other processes, and so on. In particular, nearly all of the work your application needs to do to work within the PenPoint Notebook is already implemented by pre-existing classes which comprise the PenPoint Application Framework. How Applications Work In the PenPoint™ operating system, the environment in which your application runs and how it starts up are unlike any other operating system. MS-DOS accepts a command line, executes a single program at a time, and pretty much gets out of the way while that program is running. The PenPoint Application Framework takes an active role in running your application. The Application Framework is responsible for activating, saving, restoring, and 3.:2 CHAPTER 3 I APPLICATION CONCEPTS 21 How Applications Work terminating your application. Additionally, the Application Framework plays a part in installing and deinstalling your application. ~ a. III Because all PenPoint applications use the Application Framework, all applications behave consistently. Additionally, the Application Framework handles the housekeeping functions that Macintosh or MS-DOS programs must perform ftom boilerplate code. Meanwhile, the PenPoint Application Framework presents the PenPoint user with multiple small, concurrent documents as part of a consistent, rich notebook metaphor. It's difficult to cleanly define the PenPoint Application Framework, because it is both external to your application and something your application is itself a part of. But here's an attempt: The PenPoint Application Framework is both the protocol for supporting multiple, embeddable, concurrent applications in PenPoint, and the support code that implements most of an application's default response to the protocol. To help you understand how an application fits into the PenPoint computing environment, this section walks through some important stages in the life of an application. By its end you should understand a little about the PenPoint Application Framework, some of the classes of objects in PenPoint, and why classes are so important. The next section explains class-based programming in PenPoint. With an understanding of the PenPoint Application Framework and the Class Manager under your belt, you'll be able to work through the tutorials on PenPoint programming that begin in Chapter 6. The tutorial summarizes other PenPoint subsystems: windows, User Interface (UI) toolkit, filesystem, and handwriting translation. The tutorial incorporates these subsystems into a set of increasingly functional sample programs. Installing and Starting Applications After acquiring an application, the user must install the application in the PenPoint computer. Usually an application distribution disk contains the code and data that implement the application's classes, and any other classes required by the application. We'll first look at how a user installs and starts a program on a traditional PC operating system (MS-DOS). Then we'll compare these operations with installing and running an application on PenPoint. MS-DOS Installation In MS-DOS, the user usually installs a program by copying the program from distribution disk to a hard disk. Once on the hard disk, the program does nothing until the user types a command to start the program up. u Z o u z It 22 ,PENPOINT APPLICATION WRITING GUIDE Some MS-DOS programs require the user to copy the files from to the hard disk; others provide their own installation programs, which copy the files to the hard disk and alter system configtiration parameters for their program. Installation varies tremendously from program to program. When the user types the startup command for a program, MS-DOS loads the program into memory from the hard disk and transfers control to the program. Once the program is running, it controls most of the operations of the CPU until the user leaves the program. 'Y Penp'oint Installation 3.2.3 In Pen Point, the user installs a program by opening the Connections or Settings notebook on the Bookshelf and turning to the installable software sheet, (or by inserting a disk that contains quick installer information). From the installable software sheet, the user can choose various categories of install able items, including applications, services, dictionaries, and so on. When the user turns to a page for an installable item, the Ins~aller shows all the available applications that can be installed from the currently open volumes. The user selects an item and taps the Installed? checkbox next to the item. The Installer copies the program to an area of memoty set aside for programs (the loader database) and copies other files required by the program (such as help files, application resource files, and stationery files) to the file system. From this point, running PenPoint applications differs significantly from the MS-DOS model. Once a program is in the loader database, PenPoint can transfer control directly to it; there is no intermediate step of loading the program into memory, because it is there already. PenPoint transfers control to your program for two different reasons: the user is installing your program, or the user is opening a document that requires your program (we will cover this case in the next section). 'Y Installer Responsibilities During installation, the Installer calls a standard entry point in your program (called main) in such a way that you can tell that your program is being installed. At this time, most programs create their application class and any other classes that they need. Some programs initialize files or common data structures such as dictionaries or stationery. If your application requires code for other classes (such as a special character-entry class) and resources (such as a special font), the Installer ensures that these classes and resources are present in the computer. If they are not present, the Installer copies and installs them also. In turn, these classes may require additional classes and resources, and so on. The installer keeps track of all installed applications. When the Installer initializes your application, the application specifies whether it should go in the Tools accessory palette or in the Stationery notebook, or both (or neither). Depending 3.2.4 CHAPTER 3 I APPLICATION CONCEPTS 23 Running a Pen Point Application how your application initializes itself, the user will now see the application in the Accessories window, or in the Stationery- notebook and Stationery pop-up menu. ...uE ·After installation, your code is in a similar state to an MS-DOS .EXE or .COM program that has just been loaded into memory but not yet run. However, when the MS-DOS program terminates, it removes itself from memory. PenPoint programs stay in memory until the user removes the application. Running a PenPoint Application Z o u Z 3.3 When running an MS-DOS program, the user has to find a file that contains data understood by the program. When the user decides to stop using the program, he or she must save the data to a file and then exit. If the user chooses a file that the program doesn't understand, the program might display garbled information, at best, and at worst the program might crash. ~ PenPoint takes a fundamentally different approach; the user creates a document from a list of available applications and, at some laJer time, tells PenPoint to activate the document. The user doesn't have to activate the document immediately after creating it and, in fact, can create many, many documents without activating any of them. ",. Life Cycle of a Document 3.3.1 The standard components of an application include its application code, application object, resource file, instance directory, process, and main window. The full life cycle of a document created by an application includes these operations: • Document creation (create file) • Activation (create process) • Opening (open on screen) • Closing (remove from screen) • Termination (terminate process) • Destruction (delete file) Active documents save their internal state in the file system, but this is invisible to the user: there is no need to save or load the application's state explicitly from one session to the next. ". Activating a Document When the user activates the document, Pen Point finds out from the document what application it requires and creates a process that "runs" the application (the reason for the quotes is explained below under Application Classes and Instances). When the user deactivates the document, PenPoint saves all information for the document and then destroys the application process. 3.3.2 The important thing in PenPoint is that the document remains in the computer from the time it is created until the time that the user deletes it, but the application process exists only while the document is active. 24 PENPOINT APPLICAJION WRITING GUIDE ",. Not All Active Documents are On-Screen 3.3.3 It's only when the user activates a particular document that the document has a running application process. When the user activates a document, the PenPoint Application Framework creates an application process and calls the standard entry point in your code (main) in such a way that your application can tell that it is starting an application process (and not being installed). . However, just because a document is running, doesn't mean that it must be on-screen; conversely, if a document is not on-screen, its process might still be runnmg. The most common example of this is when the user makes a selection in a document and then turns to another document (perhaps to find a target for a move or copy). The document that owns the selection must remain active until it is told to release the selection. A second example is the user chooses Accelerated Access Speed from the Access document option sheet (sometimes called hot mode), the application processes wili continue running, even when the user has turned to another page. For a third example, you might want to create a stock-watcher type program that runs in the background most of the time. This type of program will also be active but not on-screen. ",. Application Classes and Instances 3.3.4 A PenPoint computer contains only one copy of your application code in memory, but a user can simultaneously activate several documents that use your application. Pen Point can do this because your application code is a PenPoint class and an active document is an instance of your application class. When the user installs your application, your application creates your application class. When the user activates a document that uses your application, the Application Framework creates an instance of your application class. Accept this as Gospel now. We will spend pages and pages in this and other manuals explaining how this works. ". PenPoint Drives Your Application Because of all these states that an application can be in, an application can't take control and start drawing on the screen and processing input when its main function is called. Nor can your application find out on its own ifit is on-screen or should terminate. Instead it must be directed what to do by the PenPoint Application Framework. The Application Framework sends messages to documents (and hence to your application code) to initialize data, display on screen, save their state, read their state, shut down, and so on. This is why applications must be implemented as classes. For example, when a document needs to be started up to do some work, the PenPoint Application Framework sends msgAppActivate (read this as "message 3.4 CHAPTER 3 I .APPLICATION CONCEPTS 25 Application Objects app activate") to the document. When the user turns to a document's page, the PenPoint Application Framework sends it msgAppOpen. E III U A typical MS-DOS program written in C has a main routine that displays a welcome message, parses its command line, creates a user interface, initializes structures, and then waits for user input. By contrast, a PenPoint application's main routine usually creates the application object and then immediately goes into a loop waiting for messages to the application ~bject to arrive. Because all applications enter this loop, there is a routine, AppMain, which enters the loop for you. ".. Application Obiects Z ou Z ~ 3.5 Most PenPoint applications perform three minimum actions: • Respond to user and system events (including PenPoiht Application Framework messages) • Create one or more windows for user input and to display output • Create one or more objects to maintain their data. There are object classes already written in PenPoint for each of these actions: clsApp, clsWin, and clsObjeet, respectively. These classes do the right kinds of things for applications themselves, windows, and data. They provide a skeleton of correct behavior, although obviously GO's code doesn't create tie-tac-toe applications, tic-tae-toe windows, or 3x3 "board" data objects. To get the behavior you want, you often need to use descendant classes that inherit from existing classes. This section overruns with the terminology of classes. De5cendant, Inheritance, and other terms are explained in the next section, "How Classes Interact." 26 PENPOINT APPLICATION WRJTlNG GUIDE . Figure 3-1 Application, View, and Obiect Classes PenPoint provides: Objects in a running instance of your application ".. A Descendant of clsApp The PenPoint Application Framework's interactions are sophisticated and complex. You'lliearn more about them in the following sections. Applications need to behave in a standardway to work well in the framework. To simplify life for the application developer, your application class inherits most of this standard behavior from the class clsApp. dsApp handles all the common machinery of application operation, so that many applications do not need to do anything in response to messages like msgAppActivate and msgAppOpen. Applications rely on clsApp to create their main window, display the main window, save state, terminate the application instance, ~nd so on. You must write a descendant class of dsApp and create it during installation. In the example shown here, the descendant is clsTttApp. At the appropriate time, 3.5.1 The EMPTYAPP sample program in the Tutorial does nothing significant in response to any message, yet because it inherits from clsApp you can create EmptyApp documents, copy them, float them, embed them, and so on. Lots of stuff is done for you! CHAPTER 3 I APPLICATION CONCEPTS 27 Application Objects the PenPoint Application Framework sends this class a message to create an instance of the class (Tic-Tac-Toe application instance in the figure). However, you must decide when to create your application's other objects (windows and filing objects). E III U Z o u Z ".. An Instance of clsWin The PenPoint Application Framework creates a frame for your application by default. This is a window with many "decorations": a title bar, a shadow if the window is floating, optional resize corners, close box, menu bar, tab bar, command bar, etc. These decorations surround space for a client window. It is up to you to create the client window. You can also create windows to go into your frame's menu bar, tab bar, and command bar, and you can create floating windows, additional frames, and so on. Most applications create one or more windows to draw in and allow user input. 3.5.2 Frames support only one client window, but you can insert other windows inside the client window. All window classes inherit from clsWin. This class does not paint anything useful in its window, so you must either create your own window class which draws what you want or use some of the many window descendant classes in PenPoint. ~ Some Window Classes 3.5.2.1 The Tic-Tac-Toe application, shown in Figure 3-1, for example, creates several kinds of windows based on existing classes in PenPoint: • A scrolling client window (an instance of clsScrollWin), which lets the user scroll its contents • An option sheet for its options (clsOption) • An option card for the option sheet (clsOptionTable) • Various user interface component windows (clsButton, clsLabel, clslntegerField) for the option card • Menus • A Tic-Tac-Toe view (clsTttView) to display the grid and Xs and as. Like clsTttApp, you have to write the code for clsTttView and create the class at installation. Your application must create the various windows at the appropriate times, such as when it receives msgApplnit or msgAppOpen. ~ Using clsView Many applications will use clsView, a specialized descendant of clsWin, for their . custom windows. clsView associates its window with the data object it is displaying; the data object sends the view a message when its data changes. In the case ofTic-Tac-Toe, clsTttView inherits from clsView, so the Tic-Tac-Toe window is a view. In Tic-Tac-Toe, a clsTttView instance observes the data object (an instance of clsTttData). More than one view can be associated with the same data; in theory 3.5.2.2 [ 28 PENPOINT APPLICATION WRITING GUIDE two views of the Tic-T ac-T oe board could show their state in· different ways. When the data changes, all the views are notified and can redraw themselves. 'r An Instance of clsObiect 3.5.3 Instead of managing all of the data involved with an application itself, a PenPoint application typically creates separate objects that maintain and file different parts of the data. These objects respond to messages like "Save yourself' and "Restore yourself from a file." clsObject is actually the ancestor of all classes in PenPoint, including clsWin and clsApp. There is no class specifically for objects that must be. filed. Filing is such a general operation that all objects in the Pen Point operating system are given the opportunity to respond to msgSave and msgRestore messages. PenPoint supplies various descendant classes, which help in storing structured data, such as a list class (clsList), a picture segment (clsPicSeg), a block of styled text (clsText), and so on. In Figure 3-1, the data for the Tic-Tac-Toe application (the values of the nine squares) are maintained by a separate object, Tic-Tac-Toe square values, an instance of the specialized class clsTttData. ". Understanding the Application Hierarchy 3.6 You may have wondered how PenPoint keeps track of all the sections, documents, and embedded documents in a notebook if application objects are not immediately up and running when they are created. The answer is that each \ document and section in a notebook is represented in an application hierarchy in the PenPoint file system. The Notebook table of contents displays a portion of this application hierarchy. The reason it is called an application hierarchy is that the directory structure is the same as the hierarchy of documents in Pen Point (including embedded documents, accessories, and other floating documents not on a page in the Notebook). Each notebook has a directory in the file system. Within the notebook, each document or section has a directory. Within each section, each document or section has a directory. Within each document, all embedded documents have a directory, and so on. As an example, when the user creates a document in a section of the Notebook, the PenPoint Application Fr~mework creates a new application directory in that section's directory. When the application is told to save its state by the PenPoint Application Framework, the PenPoint Application Framework gives it a file to save to in that application directory. All PC operating systems have a file system, and in most you can store application data in a similar hierarchy of directories and subdirector.ies. Some may even provide a folder or section metaphor for their file system. But they do not directly weave applications into this file system. The Notebook's TOe (tap on its The application hierarchy differs from theclass hierarchy explained in the next chapter, and from the hierarchy of windows on-screen. CHAPTER 3 I APPLICATION CONCEPTS Understanding the Application Hierarchy Contents tab to move to it) shows the organization of documents in the Notebook, and this is the organization of part of PenPoint's file system. E ...u In PenPoint, the application hierarchy exists in the \PENPOINT\SYS\BOOKSHELF directory on theSelectedVolume. You can inspect the application hierarchy yourself. Modify your ENVIRON.lNI file so that the DebugSet parameter specifies, IDB800. Run PenPoint and go to the connections notebook. Using the directory view, browse through the disk volume. In the \PENPOINT directory, you should see directories called NOTEBOOK, SECTION, and so on. Compare this with the Notebook TOe. The browser shows exactly what the file system looks like, while the Notebook TOC interprets this part of the file system as the application hierarchy. Z o u Z ~ If your selected volume is your hard drive, you can also inspect this hierarchy from DOS. However, to keep path names short, all of the PenPoint directory names below \PENPOINT use two letter names. For example, the SYS directory is SS in DOS, the BookShelf directory is BF, the Notebook is NK, and so on. The Notebook's Own Hierarchy 3.6.1 The PenPoint classes and application hierarchy probably seem obscure and confusing at this point. So let's look at how the Notebook itself is written using this metaphor. Each component of the Notebook is itself a document, with its own main window, a parent window, and a directory in the file system's application hierarchy. The important concept to grasp is that there is a correspondence among: • PenPoint applications, • The functionality of the parts of the notebook metaphor, • The visual presentation of parts of the Notebook, • The PenPoint file system layout. Some of these relationships are: Running documents are instances of application classes. Functionality of notebooks, sections, and pages is delivered by application classes. Visual components of a notebook are these applications' windows. Sections and pages in a notebook are these applications' directories. Section name and page number location in a notebook combine to form a location in the file system. 29 Strange and important! 30 PENPOINT APPLICATION WRITING GUIDE This figure shows how a typical mix of applications in a running PenPoint system uses different kinds of classes. The following figures are explained in more detail in Part; 2: Application Framework of the PenPoint Architectural Reference Figure 3·2 The Pen Point Application Framework and the Notebook Hierarchy CHAPTER 3 I APPLICATION CONCEPTS Understanding the Application Hierarchy The next figures indicate how the same visual components exist in the file system, and as processes and objects. 31 ~ III figure 3-3 The Notebook Hierarchy as Mirrored by the File System U Z o u Z ~ Bookshelf ~doc.res docstate. res Notebook doc.res docstate. res Contents t t doc.res docstate.res browstate Read Me First doc. res docstate.res t S~~lCm ~ Ideas L etc... - Package Design Letter doc.res . docstate.res Suggestion doc.res docstate. res - etc ... t t 32 PENPOINT APPLICATION WRITING GUIDE Here are the same visual components as they exist in the PenPoint file system. Document Process NB Process Process 0 NB Application Class ----Notebook Contents Section Application Class packagre D'esiian LeHer .,..,.lli,.6lltin,n You can use the Disk Viewer accessory to explore the relationship between documents and the file system yourself. To view the RAM volume in the Disk Viewer, you need to set the B debug flag to hexadecimal 800 in order to view the contents of the RAM file system. The easiest way to do this is to modify the DebugFlag line in ENVIRON.INI. Class CHAPTER 3 / APPLICATION CONCEPTS 33 Understanding the Application Hierarchy Yhe Deskiop III The highest level of the application hierarchy is the Desktop. This is an application, but there is only ever one instance of it->-you can't create additional' desktops. The Desktop application manages the bookshelfs and floating applications. Its parent window is the entir~ screen of the PenPoint computer. It draws the white background. The Notebook ...uIi: Z o u Z 3.6.3 Below the Desktop's directory lies the directory of the main Notebook (and other documents on the bookshelf). The Notebook application presents the familiar visual metaphor of a notebook, with pages and tabs. All applications that "live" on a page have subdirectories in the Notebook. There are usually several notebooks on a PenPoint computer: the Main notebook, the Stationery notebook, and the Help notebook. Even the In Box and Out Box are implemented as notebooks. The Notebook document stores the section tab size, the current page shown in the Notebook, the page numbering scheme, and so on in its directory. When the user taps to turn a page in the Notebook, the Notebook traverses the application hierarchy to the next document directory and sends a PenPoint Application Framework message to that document's application to start it up. The Notebook's window covers most of the screen except for the Bookshelf at the bottom. Page-Level Applications 3.6.4 The subdirectories in the Notebook's directory relate directly to the documents and sections in the Notebook. The name of the subdirectory is the name of the document or section. Each of these subdirectories contains the filed state of an instance of a section or document. Actually, sections are documents that know how to behave in a table of contents. This table lists some of the items in the Notebook (shown in Figure 3-4, the directory in which each of the items are stored, and the class from which each item is instantiated. :Stored in Oiredury !nsh::mce uf Closs Samples Notebook Contents dsSection New Product Ideas Samples dsMiniText Package Design Letter Samples dsMiniText Suggestion Package Design Letter dsMiniNote Sedion Most applications have a menu bar. The PenPoint Application Framework supplies a set of standard application menus (SAMs), to which applications add their own menu items. The PenPoint Application Framework provides support ~ 34 PENPOINT APPLICATION WRITING GUIDE for the menus (Document, Edit, and Options) and many of the items on the menus. Applications draw in the window that the Notebook provides for them. A page-level applications's window is the Notebook area; except for the tabs area. ".. Sections 3.6.5 Sections are similar to other applications: they are instances of an application class (clsSectApp), they appear on a Notebook page, they can have tabs. A section application displays a table of contents showing the documents that are in that section: these are simply the application subdirectories in the section's own directory. One difference between a section and other applications is that a section has a special flag in its directory entry. When the Notebook is traversing the application hierarchy (to display its table of contents, or turn to the next page), if it comes across a section it descends into the section. This enables the Notebook to number pages correctly. Actually, a section has a special directory attribute. Section data stored in the section's directory entry includes the state of its table of contents view (expanded or compressed). The Notebook Contents page is an instance of clsSectApp, just like other sections. To show the TOC for everything in the Notebook, it must have everything in the Notebook in its directory. This is why the actual file system organization of the application hierarchy is \PENPOINT\SYS\MY_NOTEBOOK\NOTEBOOKAPPS\ ... Where NOTEBOOKAPPS is the directory of the "section" for the entire Notebook contents. ".. Floating Accessories 3.6.6 Most PenPoint applications are part of the Notebook. But some applications, such as the calculator, the disk viewer, and the snapshot tool, don't "live" on a page in the Notebook. These accessories "float" on the Desktop when active, appearing over pages in the Notebook. Their parent window is the Desktop, not the Notebook page area. They aren't part of the Notebook's table of contents and you can't turn the page to them. However, a floating application is still part of the same underlying model: it has a directory (it's just not a subdirectory of the Notebook), it is sent messages, and so on. ".. Embedded Applications It is possible to embed documents in other documents which permit it. For example, an oJ;l-line "electronic newspaper" document might install an instance of a crossword puzzle application in itself; the crossword puzzle class might allow the user to embed an instance of a text application in a crossword puzzle document to let the user jot down notes and guesses. The design of PenPoint makes it easy to write applications which can embed, and be embedded in, other applications. 3.6.7 CHAPTER 3 / APPLICATION CONCEPTS Understanding the Application Hierarchy 35 When the user creates a new document in the notebook, PenPoint actually embeds the application in the Notebook application. This document embedded in the notebook is called a page-level application. E III U Z o u Z Only page-level applications appear in the Notebook's Table of Contentsapplications that are embedded in page-level applications do not. It doesn't make sense for a user to turn the page to an application embedded in the current page. ~ Application embedding is very straightforward. When the user moves or copies an application, the Desktop application sends a msgAppCreateChild message to the destination application. If the application permits embedding, the PenPoint Application Framework handles this message by creating a directory for the embedded application within the destination application's directory. When an application is embedded in another, the embedded application is inserted into two hierarchies: the file system hierarchy and the window system hierarchy. In the file system, the application directory for an embedded application is a subdirectory of the application directory of the application in which it is embedded. In the window system, the parent application supplies a window into which the embedded application can insert its main window. Thus, in our example, the newspaper application uses an application directory for the newspaper document. Within that directory is an application directory for the crossword document. Within the crossword application directory is a directory for the text editor document. The newspaper document window contains a window which is the main window for the crossword document. The crossword docurnent window contains a window which is the main window for the text editor. Application Data 3.6.8 A document stores data in its directory so that when its running process is terminated, its state lives on in the file system. The Application Framework can later create a new process for the document and direct the document's application to restore the document from this filed state. Some information is of interest to this instance only, such as the visible part of the file, the user's current selection, and so on. This would probably be saved by the application itself, that is to say, when the application receives msgSave it writes this information out. The application can also tell the Application Framework to send msgSave to other objects to get them to save their data (your application can't send msgSave directly to another object). For example the image in a sketching program might be implemented as a separate object; when the application is told to save, it tells the Application Framework to save the image object. By default dsApp saves the information about the document, including its comments, frame window position, mode, and so on, so you only need to save those things created by your application class. There are many mechanisms that automatically propagate msgSave to related objects. Frames can be set to save child windows, views save their data objects, and so on. 36 PENPOINT APPLICATION WRITING GUIDE Activating and Terminating Documents 3.1 In the section "Application Classes and Instances," we described how an instance of the application is created. The previous section should help clarify the relationship between the file system and an instance of your application. The location of a document in the file system hierarchy has a one-to-one correspondence with its location in the Notebook, on a page, within a section, and so on. See the figure to get a sense of the relationship. The main determinant of how and when documents blossom from being directories and data in the file system to being live running processes and objects is the user's action of turning the page. When the user turns to a page, the documents on that page become visible; if they aren't already running, the Application Framework activates them. Turning a Page and msgAppClose 3.1.1 When the user turns to another page, the document on the original page no longer needs to appear on screen, so the PenPoint Application Framework sends msgAppClose to the application instance, indicating that it can close down its user interface. When it receives msgAppClose, the application might still have some processing to do or it might be talking to another application. The application can finish its work before acting in response to msgAppClose. To respond to msgAppClose, the application should save (to the file system) any data about on-screen objects that the user moved or changed. The application should then destroy and remove all windows that it created, thereby reducing memory usage. An application instance may receive msgAppTerminate after msgAppClose (ifit isn't in "hot mode"). When it receives msgAppTerminate, the application must save all data that will be required to restore the document to the screen exactly as it was before, because msgAppTerminate kills the document's process. Restoring Inactive Documents When the user turns back to the saved document, the Application Framework looks at that document's directory. If the process for the document was terminated, the Application Framework starts a new process, creates a new instance of the application class, and recreates the document based on information in the directory. As part of this re-creation, the Application Framework sends the document msgRestore, which tells it to read its state back in from the file system. The Application Framework then sends msgAppOpen to the application, telling it to prepare to draw on the screen. The Application Framework also sends msgRestore and msgAppOpen to any embedded applications in that document. Finally the Application Framework inserts the.application's windows into the screen, and the windows receive messages telling them to paint. 3.1.2 CHAPTER 3 / AP'PLICATION CONCEPTS 37 Documents, not Files and Applications From this point the user can interact with the document. When the user makes a gesture within the document, the document's application controls the resulting action. ...u~ Z o u ". Page Turning instead of Close 3.7.3 .fu described in "Turning a Page and msgAppClose," most PenPoint applications don't need a Close menu item. Most documents are active until the user turns the page; others may be active even when off-screen (for instance, if they have the selection or are involved in a copy operation). The user doesn't know what a running application is: when the user turns to a page, everything on it appears exactly as it was when the page was last "open," and every window responds to the pen. The fact that some of the applications may have been tunning all the time while others were terminated and restarted should be inconsequential to the user. ", Saving State (No Quit) 3.7.4 In an MS-DOS or Macintosh program, the user explicitly Quits the application, and thus doesn't expect the application to reappear in exactly the same state. Because of PenPoint's notebook and paper paradigm, you must preserve all the visual state of your application so that when it is restarted it appears the same. This has strong implications for the kinds of information your application needs to save when an application receives msgSave. Documents, not Files and Applications It's important to understand that the application instance and the file it is editing are conjoined. The user should rarely, if ever, see "files," instead she or he sees only documents. (The exception to this is when importing data from and exporting data to other computers.) Ordinarily, for every document in the application hierarchy there is an application. A user can deinstall an application without deleting the application's documents in the file system. If the user tries to turn to one of these documents, there is no code to activate them. Instead, these orphan applications are handled by a "mask" application that tells the user that the application has been deinstalled and prompts the user to reinstall the application. No New, No Save As ••• On a Pc, the user usually starts an applic:ltion, and then chooses what file to open with that application. But in the PenPoint operating system, the user can start an application by: • Turning to the page that contains a document, • Floating a document, • By creating a new embedded document. 3.8 Z ~ 38 PENPOINT APPLICATION WRITING GUIDE The document open on a page (and any floating or embedded documents on that page) are all applications with open files. You do not open a file from within an application. Instead, you turn to (or float or embed) another document and Pen Point starts up the correct application for that document. Thus, it does not make sense to try to open another document from the current application, or to save the current document as another document. The only time that an application needs to actually open a file from disk is when it is importing data from or exporting data that will be used by a file-oriented program on a file-oriented operating system. Stationery 3.8.2 Users often want new instances of an application to start off from a particular state. Instead of opening a template from within the application, Penpoint supports application-specific stationery. The default piece of stationery is an application instance started from scratch. The user can create additional stationery documents, which are just filed documents kept in a separate notebook. In the case ofTic-Tac-Toe, each document shows a view of its own board. There is no new command, because the user can always create a new document. There is no save command either-the Tic-T ac-T oe state is saved on every page turn. There's no open command, because the user can either turn to another Tic-Tac-Toe's page to "read it in," or can start from a desired template by accessing documents in the Stationery menu or auxiliary notebook. Shutting Down and Terminating Applications 3.9 If a document is in the application hierarchy, it always exists as a directory in the file system, whether it has a running process or not, and whether it is visible or not. When the user deletes a document (page-level or embedded), Pen Point deletes its directory from the file system. The user can also elect to use the installer to deinstall or deactivate an application. This might be necessary when the user needs more room on the computer for a different application, or when the user isn't using an application any more. Deinstallation removes all application code from the loader database, which prevents the user from running it. However, the documents still exist in the application hierarchy, and can spring back to life if and when the user re-installs the application. Deactivation also removes the application code, but PenPoint remembers where the application came from, so that it can prompt the user to insert the appropriate disk if the user chooses to reactivate the application. While the application is not available, the mask application handles the application's documents. Conserving Memory 3.9.1 When a document is active, it is obviously consuming memory, but when it is not active, it can still consume memory (if the computer is using a RAM-based file system). The document's saved state is in the application hierarchy, which can be CHAPTER 3 / APPLICATION CONCEPTS Shutting Down and Terminating Applications in the RAM file system; the RAM file system shares RAM with running processes. This emphasizes how important it is to conserve memory. 1/1 ...uIi: You should also try to conserve memory when an instance is running but not open (for example, if it has the selection but is off-screen). This is an opportunity to destroy UI controls and other objects which are only needed when your application is on screen. Avoiding Duplication Documents receive messages from the Application Framework telling them to save their state to their directory. When a document starts up, its corresponding application often reads all of this state back into memory. This means that there are two copies of the document's state, the one in its address space and the saved copy in the file system. This can be quite wasteful of space. There are several approaches to eliminating this redundancy: • Don't read state back into memory. Read information in from the file system when needed. This works well for database~type objects. Because the application hierarchy is in memory, file I/O is faster than you might think, but this is still slow. It does prevent the user from reverting to the filed state of the document, since the filed state is always being updated. Your application would have to disable Revert, or make its own backup copy of filed state. • Use memory-mapped files to map filed state into the application's address space. This works well for large data files, but it does interfere with Revert. • Read state back into memory, then delete the information from the file system. This means that if the application instance crashes, there is nothing in the file system to recover. • Refuse to save state to the file system. This implies that the application process can't be terminated. This also means that the application state can't be recovered. Hot Mode The last alternative above is supported by the PenPoint Application Framework. An application class or the user (by choosing Accelerated for Access Speed in the application's option sheet) can tell the PenPoint Application Framework that an application instance should not be terminated. This is called "hot mode." It means that the document will appear much faster when the user turns to it, because its process never went away. Ordinarily the Application Framework must start a new process, create a new application object, tell it to restore its state, then put it on-screen. 39 Z o u, Z 3.9.2 ~ 40 PENPOINT APPLICATION WRITING GUIDE ",. .Components As we have seen, you can embed applications within other applications. This is the basis for the Application Framework's hierarchy. Applications require a good deal of overhead: each has its own directory, has code in the loader database, and runs ' as its own proceSs (in addition to the directories and processes used by that application's documents). You can reduce the size of an application by using components. Components are separate DLL's that provide a well-defined API to their clients. Most components can be used as part of an applications, but they don't require much overhead. Components don't run as a separate process, and don't have a separate directory. Some components, such as Reference buttons, manifest themselves as visible objects and let the user embed, move, and copy them. Others, such as text views, are visible but can be added to applications only programmatically. Others, such as the Address Book, do not even have a VI; that is, they do not display on screen (the address book provides information that other applications then format and display). 3.9.4 Chapter 4 / PenPoint Class Manager The previous chapter introduced some of the concepts in the PenPoineM Application Framework. This section quickly covers the PenPoint operating system's object-oriented Class Manager. The Application Framework largely determines the overall structure of your applications; sending messages to objects using the Class Manager makes up 80% of the line-by-line structure of your code. With an understanding of the Application Framework and the Class Manager, you can start the tutorial. There are three elements to the PenPoint operating system's object-oriented software environment: objects, messages, and classes. Perhaps the simplest way to introduce the concepts of objects, classes, and messages is by looking at an example. The example discussed in the next three sections outlines what must happen to set the title of an icon. A user sets an icon's title by making the y/ gesture over it. When its option sheet appears, the user enters a new icon title, makes sure the layout style is one that includes the title, and taps Apply. If you feel that you understand the concepts of object-oriented, message-passing, class-based systems, you can skip this introduction and go directly to the section titled "Sending a Message." ,. Obiects Instead of Functions and Data In a non-object-oriented system, the icon and its title would be stored in a data structure. Any piece of code that gets or sets information pertaining to the icon must know the exact organization of that data structure. To modify- the icon title, the program would locate the data structure that represented the icon; for example, it might change the icon's title string by changing a pTitleString pointer. This program will break if the internal structure changes or if the string is later implemented by storing a compact resource identifier. In an object oriented system, anything in the system can be an object. In our example, the icon is represented by an object. The object knows about both the data for an icon and the functions that manipulate it. The object hides, or encapsulates, the details of its data structures and implementation from clients. One of the messages understood by the object might be "Set Your Title String," which tells the object to change its title. Because the object contains the code for the functions that manipulate it, the object locates its own internal data structures that represent the title, and changes the title. 4.1 42 PENPOINT APPLICATION WRITING GUIDE This encapsulation reduces the risk of clients depending, either deliberately or accidentally, on implementation details of a subsystem. If the internal structure changes, only the object's code that manipulates the structures must change. Any client that sends the "Set Your Title String" message can still send that message and will still get the same effect. Note "Client" here and elsewhere in the SDK documentation means any code making use of a software facility. Some objected-oriented systems, including PenPoint, use software and hardware protection facilities to prevent clients from accessing or altering the internal structures of objects, whether accidentally or maliciously. Ideally, in object-oriented operating systems, the objects presented to clients should model concrete ideas in the application. For example, if your application's user interface requires a button, it should create an object for that button; if your application has a counter, it should create an object to maintain that counter value. Messages Instead of Function Calls Modular software systems are sometimes object-oriented without being message-passing. That is to say, they have objects that hide data structures from clients (such as "window"), and you pass these software objects as arguments to functions which act on them. Using the example of setting an icon's string, in such systems you might pass the icon_window object to a routine called Window_SecString. But this approach requires that clients know which function to call, or that the function handle many different kinds of objects. The implementation of icon strings might change so that icons need to be handled specially by a new Icon_Set_String function. Again, all clients would have to change their function calls. Message-passing systems flip this control structure so that the object hides the routines it uses. A client simply sends a message to the object, and the object figures out what to do. This is known as data encapsulation. In the example we're using, clients send the message msgLabelSetString to the icon; the only argument for the message is a pointer to the new Title string. Because icons (or other objects) respond to messages, it doesn't restrict the implementation of icons: if, in the future, icons handle titles differently than other labels, they can still respond to msgLabelSetString correctly. "The object figures out what to do" sounds like black magic, but it is actually not very complicated. You call a C routine to send a message to an object. Inside the Class Manager code, the Class Manager looks up that message in a table (created by the developer of the icon class) that specifies what function to call for different messages. If the message is in the table, the Class Manager then calls the icon's internal function which actually implements the message. 4.2 CHAPTER 4 / PEN POINT CLASS MANAGER 43 Classes Inst.ead of Code Sharing One benefit of using messages instead of function calls is that many different objects can respond to the same message. All objects that come from a common ancestor will usually respond to the messages defined by that ancestor. For instance, you can send msgLabelSetString to almost any object. (In some systems this is called "operator overloading.") You can send any message to any object. Depending on whether it knows how to respond to the message, the object chooses what to do: • If the object understands the message and can handle it, the object processes the message. • If the object doesn't understand the message, it gives the message to its ancestor, to see if its ancestor knows how to handle the message (more on ancestors later in this section). • If the object understands the message, but doesn't want to handle it, the object can ignore the message (by returning a non-error completion status), reject the message (by returning an error completion status), or give the message to its ancestor. Classes Instead of Code Sharing Icons and several other similar objects have titles. Thus, each of those objects that has a modifiable title must handle the "set string" message in some way or other. In other programming methodologies, programmers take advantage of functional overlap by copying function code, trying to make data structures conform so the same routine can be used, or calling general routines from object-specific routines. However, whether you copy code or link with general routines, the resulting executable file contains a static copy of the shared code. The best you can hope for is shared code implemented by the system, which is rare. In a class-based system, an object is an instance of a specific class. The class defines the data structures that are used by its instances, but doesn't necessarily describe the data in the structures (it is the data stored in these structures that differentiates each instance). The class also contains the functions that manipulate the object's data. Each instance of a class contains the data for the specific thing being described (such as an icon). Each instance also knows to which class it belongs. Thus, there can be many instances of a class (and data for each instance), but the code for that class exists in only one place in the entire system. If an existing class does almost everything you want, but not quite, you can create a new class that inherits its behavior from the existing class. The new class is said to be a subclass or descendant of its ancestor class. The subclass contains unique functionality that was not previously available in its ancestor. The subclass should not reproduce anything that was defined by its ancestor. The subclass only defines the additional data structures required to describe the new thing and the functions required to handle messages for the new thing. An object can respond to any message sent to it; the message does not have to be defined by the object's class or its ancestor class. 44 PENPOINT APPLICATION WRITING GUIDE Of course, subclassing does not stop at one generation. The icon window class, for example, has eight ancestors between it and clsObject, which is the fundamental class for all classes in PenPoint. Take a look at the PenPoint Class Hierarchy iOn the class hierat:chy poster. Find the relationship between clslcon and clsLabel (they're near the lower right edge). Handling Messages 4.3.1 An object can send a message to its ancestor class either when it doesn't recognize the message or when it chooses to allow its ancestor class to handle the message. Remember that even when the ancestor handles the message, it uses the data for the object that initially received the message. Because the icon window class inherits from the window class, the icon window automatically responds to all the messages that a window responds to (such as msgWinDelta to move it or msgWinSetVisible to hide it) in addition to all the messages specific to an icon window (such as msglconSetPictureSize). A class can override or change some of its ancestors' messages; for example, the icon window responds to msgWinRepaint by letting its ancestor label paint the string, then it draws its picture. By making it very easy to inherit behavior from existing classes, class-based systems encourage programmers to extend existing classes instead of having to write their own software subsystems from scratch. If you create a new kind of window, sayan icon with a contrast knob, you can make it a descendant of CHAPTER 4 / PEN POINT CLASS MANAGER Sending a Message another class, and it will inherit all the behavior of that class, or as much behavior as you choose. You may find it easier to understand class-based programming by viewing code instead of reading abstract explanations. The next few pages give some simple examples of using messages and classes, and even the very simplest program in the tutorial is-has to be!-fully class-based. Sending a Message In PenPoint, you usually send a message to an object using the ObjectCall function (if the object is owned by another process you use ObjectSend). The differences between ObjectCall and ObjectSend are detailed in Part 1: Class Manager, of the PenPoint Architectural Reference. Here's a real-life example of sen~ing a message. PenPoint provides a utility class, clsList, which maintains a list object. The messages that clsList responds to are documented in Part 9: Utility Classes, of the PenPoint Architectural Reference and in the clsList header file (\PENPOINT\SDK\INC\LIST.H). This is the definition of msgListAddItemAt from LIST.H: /**************************************************************************** msgListAddltemAt takes P_LIST_ENTRY, returns STATUS Adds an item to a list by position. ****************************************************************************/ #define msgListAddltemAt MakeMsg(clsList, 10) Don't worry about the details of the definition right now; this just tells us that msgListAddItemAt is defined by clsList, that the message uses a P_LIST_ENTRY structure to convey its arguments, and that the message returns a value of type STATUS when it completes. We want to send msgListAddItemAt to a list object, telling it to add the value 'G' to itself at position three in the list. Message Arguments Now, in order for a list object to respond appropriately to msgListAddItem, it's going to need some additional information. In this case the additional information is the item to add to the list ('G'), and where to add it (third postion). Most messages need certain information for objects to respond correctly 45 46 PENPOINT APPLICATION WRITING GUIDE to them. The information, called message arguments, you pass along with the message. to the recipient In this case, the header file informs us that msgListAddItemAt takes a P_LIST_ENTRY. In Pen Point's C dialect, this means "a pointer to a LIST_ENTRY" structure. Here's the structure: typedef struct LIST ENTRY U16 LIST ITEM position; item; The use of a weak word like "takes" is deliberate. Although a class usually reqUires a specific message argument structure, there is no mechanism available to detect when you pass it the wrong structure. LIST_ENTRY, *P_LIST_ENTRY; typedef P_UNKNOWN LIST_ITEM, *P_LIST_ITEM; UI6 is an unsigned 16-bit number, P_UNKNOWN means a 32-bit pointer to an unknown. (Chapter 5 of this manual describes the rest of PenPoint's ubiquitous typedefs and #defines.) When you can deliver the message and its arguments to a list object, you're set. Here's the C code to do it: LIST LIST ENTRY STATUS list; add; s; II II II the object structure for message arguments most functions return a STATUS value II II Add an item to the list: 1. Assemble the message arguments; add.position = 3; add. item = (P_LIST_ITEM)'G'; II 2. Now send the message and message arguments to the object. if ((s = ObjectCall(msgListAddItemAt, list, &add)) != stsOK) { Debugf (" add item failed: status is: Ox%lX", s); ",. ObiectCall Parameters 4.4.2 The code fragment above assumes that the list object (list) has already been created; object creation is covered later in this chapter. As you can see, ObjectCall takes three parameters: The term "parameters" is used in function calls; the term "arguments" is used for data reqUired for a specific message. • The message (msgListAddItem). Messages are just 32-bit constants defined by a class in its header file. You can send an object a message defined by any of the classes from which it inherits. (Some objects even respond to messages defined by classes that are not their ancestors.) We use bold face to indicate items defined by PenPoint and other symbols used in examples. • The object (list). Objects are referenced by UID's, unique 32-bit ID numbers. UID's are discussed in more detail later. • The arguments for the message (add). Not all messages take arguments (msgFrameClose, for example, takes none), but others do (msglconSetPictureSize, for example, takes a width and height). The PenPoint Architectural Reference manual and the header files (in this case, \PENPOINT\SDK\INC\LIST.H) document each message's arguments. CHAPTER 4 I PENPOINT CLASS MANAGER Sending a Message 47 ObjectCall has one 32-bit parameter for all the message's arguments; if a message takes more arguments than can fit in 32 bits, you must assemble the arguments in a structure and pass ObjectCall a pointer to the structure. In this case, msgListAddltem takes a·P_LIST~ENTRY, a pointer to a LIST_ENTRY structure. (The PenPoint convention is that a type that begins with P_ is a pointer to a type.) Hence the address of the add structure (&add) is passed to ObjectCall. "'''' Returned Values 4.4.3 The result of sending a message is returned as a status value (type STATUS). stsOK ("status OK") is zero; All status values that represent error conditions are less than zero. Note that STATUS is a 32-bit quantity, hence the %JX in the Debugf statement to print out a long hexadecimal. Some messages are designed to return errors that you should test for. For example, the status returned by sending msglsA to an object is stsOK if the object inherits from the specified class, and stsBadAncestor if the object does not. Some objects respond to messages by returning a positive value (which is not a status value, but an actual number). Others return more complex information by filling in fields of the message argument structure supplied by the caller (or buffers indicated by'pointers in the message argument structure) and passing back the structure. ".. How Obiects Know How to Respond 4.4.4 The list object responds to msgListAddltem because it is an instance of clsList. But what does that mean? The list object has several attributes. Among them are the class that created the object and the instance data for that object. As described above, when you define a class, you must also cre~te a table of the messages handled by your class. The Class Manager finds out which class created the object and looks for the method table for that class. The method table tells the Class Manager that the class has a function entry point for that message, so the Class Manager calls that function entry point, passing in the message and the message argument structure. Although the object receives the message, its class has the code to handle the message. If the class decides to give the message to its ancestor, it passes the message and the message arguments to the ancestor (but the instance data is still the instance data for the object that received the message). The Class Manager gives the object a painter to the object's instance data. This is one aspect of PenPoint's data integrity. 48 PEN POINT APPLICATION WRITING GUIDE, Creating an Obiecl Where,did the list object in the example above come from? The short answer is that a client asked dsList to create an instance of itself by sending msgNew to dsList. In many ways this is no different than when we sent msgListAddltem to the list object in the previous example. Classes and Instances The longer answer involves understanding the relationship among classes and instances. In the section "Sending a Message" we discussed the fact that you send messages to objects and those objects respond to the messages. We also discussed how a class describes the data structures and the code used by its instances. A class responds to msgNew by manufacturing an instance of itself. What is an instance? It is merely an identifier and the data structures that represent an object. Thus, the class asks the Class Manager to allocate the data structure and assign an identifier to the structure. How can a class respond to a message? This is a fundamental concept and one that is hard to understand at first: a class is an object, just like any other PenPoint object. And just like any other PenPoint object, an object is an instance of a class. In the case of classes, all classes are instances of dsClass. In other words, all classes are objects, but not all objects are classes. You can think of classes as objects that know how to create instances. When a client sends a message to a class, the class behaves like any other object and allows the class that created it (dsClass) to handle the message. dsClass contains the code that creates new objects. Thus, in answer to our original question about how did the list object come into being: a client sent msgNew to the object named clsList. clsList is an instance of clsClass, so the code in clsClass created a new object that is an instance ofclsList. An Alternative Explanation At an implementation level, here's what actually happens. The PenPoint Class Manager maintains a database of data structures; each data structure represents an object. The PenPoint Class Manager locates these objects When the object created by clsClass is an instance of clsClass, the new object is a class. CHAPTER 4 I PEN POINT CLASS MANAGER Creating an Object 49 by 32-bit values, called UIDs (unique identifiers); UIDs are explained later in this chapter in "Identifying the New Object: UIDs." The data structure for each object contains some consistent information (defined by clsObject) that indicates the class to which the object belongs and other attributes for the object. Other information in the data structure varies from object to object, depending on which class created the object. ... lIIi: (!) ct ct Z :::e III III When a client sends a message to an object, the Class Manager uses the UID to locate the object. The Class Manager then uses the object's data structure to find the class that created the object. The Class Manager finds the class and uses the class's method table to find the entry point for the function that handles the message. S U To create an object, the process works the same way. A client sends msgNew to a class object. The Class Manager locates the object, finds the class that created the object (clsClass), and calls the function in clsClass that creates new objects. The NEW Structure You send msgNew to nearly every class to create a new instance of that class. In The exceptions are pseudo the case of msgNew, the message argument value is always a pointer to a structure classes and abstract classes (see the Glossary). that defines characteristics for the new object. This structure is commonly called the class's _NEW structure because the name of the structure is a variation of the For many classes, the _NEW structure is identical to the class name, followed by _NEW. For clsList, the _NEW structure is LIST_NEW. structure that contains the object's metrics. The _NEW structure is mainly used to initialize the new instance. For example, when creating a new window you can give it a size and specify its visibility. The _NEW structure differs depending on the class to which you send it. You can find the specific _NEW structure to use when creating an instance of a class by looking in the PenPoint API Reference manual or in the class's header file. For clsList, messages and message arguments are defined in \PENPOINT\SDK\INC\LIST.H. The _NEW structure is LIST_NEW. This excerpt comes from the LIST.H file: typedef struct LIST_NEW_ONLY LIST_STYLE style; LIST_FILE_MODE fileMode; U32 reserved [4] ; LIST_NEW_ONLY, *P_LIST_NEW_ONLY; #define listNewFields objectNewFields LIST NEW ONLY typedef struct LIST_NEW listNewFields LIST_NEW, *P_LIST_NEW; \ \ list; II Filing mode. II Reserved 50 PENPOINT APPLICATION WRITING GUIDE Reading the _NEW Structure Definition 4.5.3.1 To read the _NEW structure definition, you need to perform the work that the compiler does in its preprocessor phase, expanding the macro definitions. The _NEW structures in the PenPoint API Reference have all been expanded for your convemence. Start by looking for the typedef for the _NEW struct (typedef struct LIST_NEW) at the end of the example. The structure is represented by a :itdefine name (in this case listNewFields). Here's where it gets tricky; start thinking about inheritance. The :itdefine name (listNewFields) has two parts: • The :itdefine name for the NewFields structure of the class's immediate ancestor (in this case, objectNewFields, which defines the arguments required by dsObject) . • A _NEW_ONLY structure for the class being defined (UST_NEW_ONLY). The UST_NEW_ONLY structure contains the actual msgNewarguments required for dsList. Each subclass of a class adds its own _NEW_ONLY structure to the NewFields #define used by its immediate ancestor. This is how the _NEW structure for a class contains the arguments required by that class, by its ancestor class, by that class's ancestor, by that class's ancestor, and so on. In this case, however, there is only one ancestor, dsObject. objectNewFields is defined in \PENPOINT\SDK\INC\CLSMGR.H: #define objectNewFields OBJECT_NEW_ONLY is defined in the same file. It has many fields: typedef struct OBJECT_NEW U32 newStructVersioni OBJ KEY OBJECT uidi OBJ CAPABILITY CLASS objClassi OS- HEAP- ID heapi U32 U32 spareli spare2i keYi capi II II II II II II II II II II II II II II Out: [msgNewDefaults] Validate msgNew In: [msgNew] Valid version In: [msgNew] Lock for the object In: [msgNew] Well-known uid Out: [msgNew] Dynamic or Well-known uid In: [msgNew] Initial capabilities Out: [msgNewDefaults] Set to self In: [msgObjectNew] Class of instance In: [msg*] Used by toolkit components Out: [msgNewDefaults] Heap to use for additional storage. If capCall then OSProcessSharedHeap else OSProcessHeap Unused (reserved) Unused (reserved) OBJECT_NEW_ONLY, OBJECT_NEW, * P_OBJECT_NEW_ONLY, * P_OBJECT_NEWi Most elements in an argument structure are passed In to messages-you're specifying what you want the message to do. Out indicates that an element is set during message processing and passed back to you. In:Out means that you pass in an element and the message processing sets the field and passes it back to you. CHAPTER 4 I PENPOINT CLASS MANAGER Creating an Object A J,IIIEW3:1NLY for lEach C~ass 4.5.3.2 Why such a complicated set of types? Thanks to class inheritance, when you create an instance of a class, you are also creating an instance of that class's immediate ancestor class, and that ancestor's ancestor class, and so on up the inheritance hierarchy to the root Object class. Each ancestor class typically allows the client to initialize some of its instance data. Many classes allow you to supply the msgNew arguments of their ancestor(s} along with their own arguments. This is true for clsList: it inherits from clsObject (as do all objects) and part of its msgNew argument structure is the OBJECT_NEW argument structure for clsObject. clsList has threemsgNew arguments of its own: how it should file the entries in the list, a list style, and a reserved U32. These large message arguments structures are intimidating, but the good news is that by sending msgNewDefaults, you get classes to do the work of filling in appropriate default values. You then only need to change a few fields to get the new object to do what you want. ".. Identifying _NEW Structure Elements 4.5.4 As a class adds a_NEW_ONLY structure to a _NEW structure, it also gives a name to the _NEW_ONLY structure. From the clsList example, we can expand the LIST_NEW definition as: . typedef struct LIST_NEW objectNewFields LIST NEW ONLY LIST_NEW, *P_LIST_NEW; list; The name list identifies the LIST_NEW_ONLY structure within the LIST_NEW structure with the name list. We can carry on the expansion to apply the definition of objectNewFields: typedef struct LIST_NEW { OBJECT NEW ONLY LIST NEW ONLY } LIST_NEW, *P_LIST_NEW; object; list; You can see now, when you create an identifier of type LIST_NEW, you can specify the _NEW_ONLY structures by specifying their names. For example, if your code contail)s: LIST_NEW myList; You can refer the LIST_NEW_ONLY structure by myList.list, and the OBJECT_NEW"':ONLY structure by myList.object. to ,.,. Code"to Create an Obiect This example code creates the list object to which we sent a message in the first code fragment. Later code will show how the list class is itself created. The preceding discussion mentioned that the client sends msgNew to a class to create an instance of the class. The function parameters used in ObjectCall for 4.5.5 51 52 PENPOINT APPLICATION WRITING GUIDE msgNeware the same as before (the object to which you send the message; the message, and the message argument value). As we have seen, the _NEW structure can get quite large (because most subclasses add their own data fields to the _NEW structure). Many classes have default values for fields in the _NEW structure, yet clients must be able to override these defaults, if they want. To initialize the _NEW structure to its defaults, clients must send msgNewDefaults to a class before sending nisgNew. msgNewDefaults tells a class to initialize the defaults in the _NEW structure for that class. Mter msgNewDefaults returns, the client can modify any fields in the _NEW structure and then can call msgNew. LIST list; LIST NEW new; STATUS S; II Object we are creating. *1 II Structure for msgNew arguments sent to clsList. II Initialize _NEW structure (in new). ObjCallRet(msgNewDefaults, clsList, &new, s); II Modify defaults as necessary ... new.list.fileMode = listFileItemsAsData; II Now create the object by sending msgNew to the class. ObjCallRet(msgNew, clsList, &new, s); II The UID of the new object is passed back in the _NEW structure. list = new.object.uid; Because almost every message returns a status value (to say nothing of most function calls), your code tends to become littered with status checking. Hence \PENPOINT\SDK\INC\CLSMGRH defines several macros to check for bad status values. This fragment uses one of those macros, ObjCallRet. ObjCallRet does a standard ObjectCall with its first three parameters, and assigns the return value to its fourth. If the returned value is less than stsOK, ObjCallRet prints a warning (when compiled with the DEBUG flag) and returns the value to the caller of the function. There are many other macros of a similar nature; they are documented in Part 1: Class Manager of the PenPoint Architectural Reference. ".. Identifying the New Obiect: UIDs When you send msgNew to a class, the message needs to give you an identifier for the new object (so your code can use it). As mentioned above, messages often pass back values in the structure that contains the message arguments. In this case, clsObject passes back the UID ofthe newly created object in its OBJECT_NEW structure (in object.uid). In our code example, the UID for the new object was passed back in new.object.uid. The sample copied the value to the object named list, and henceforth uses list when refering to the new list object. You refer to objects using llDs. A UID is a 32-bit number used by the Class Manager to indicate a specific PenPoint object. An object's UID is nota C pointer; it consists of information used by the Class Manager to find an object Status values less than stsOK indicate errors. 4.5.6 CHAPTER 4 I PENPOINT CLASS MANAGER 53 Creating a Class and information about the object's class and other things. The symbol list in this example is the UID of our list object; clsList is the UID of the list class. PenPoint defines many classes that clients can use to create instances for their own use (such as the list class, the window class, and so on). All of these built-in classes are depicted in the class hierarchy poster. When a client sends msgNew to a class to create a new object, the class is identified by a unique value. If ail application knows this value and the class is loaded in PenPoint, the application can create an instance of the class. This value is called a global well-known UID. ... ~ There are other types of UIDs: local well-known UIDs and local private UIDs. There are no global. pd,_UID. The global well-known UIDs of all the public PenPoint classes, including clsList, are defined in \PENPOINT\SDK\INC\uID.H. Because all PenPoint programs include this header file when they are compiled, all programs know about these classes. clsList is defined with this line in UID.H: tdefine clsList MakeWKN(lO,l,wknGlobal) MakeWKN (pronounced "Make well-known") is a macro that returns a 32-bit constant. Here the parameters to MakeWKN mean "create a well-known UID in global memory for version 1 of administered ID 10." No other well-known UID uses the number 10. Eventually, when you finalize your application, you will need to define your own well-known UIDs. To get the administered number, contact GO Developer Technical Support; they will assign you a specific administered value. Until that time, you can use some spare UIDs, defined in \PENPOINT\SDK\INC\UID.H, for this purpose. These UIDs have the values wknGDTa through wknGDTg. ".. Creating a Class You have seen how to send a message to an object and how to send msgNew to a class to create a new object. You use the same procedure to create any object and send it messages, So you can send messages to any instance of any class in PenPoint. The last step is to create your own classes for your application. At the very least you must create a class for your own application; frequently, you will also create special window classes and data objects that draw and store what you want. Creating a class is similar to creating an instance, because in both cases you send msgNew to a class. When you create a class, you send msgNew to clsClass. This is the class of classes. Remember that a class is just an object that knows how to create instances of itself; in this case clsClass knows how to create. objects which themselves can create objects. 4.6 ~ ; II 1ft 1ft ~ 54 PEN POINT APPLICATION WRITING GUIDE In short, to create a class, you send msgNew to clsClass, and it creates your new class object. A routine much like this in the PenPoint source files creates clsList; it is executed when the user boots PenPoint (when the SYSUTIL.DLL is loaded). Some classes, such as clsList are created at boot time; other classes are created later,such as at application installation. 1***************************************************** *********************** ClsListInit Install clsList ****************************************************************************1 STATUS ClsListInit (void) ( CLASS NEW STATUS neWi Si ObjCallRet(msgNewDefaults, clsClass, &new, S)i new.object.uid clsListi new.class.pMsg (P_MSG) ListMethodTable; new.class.ancestor clsObject; new.class.size SizeOf(P_UNKNOWN); new.class.newArgsSize SizeOf(LIST_NEW); ObjCallRet(msgNew, clsClass, &new, S)i return stsOK; II ClsListInit New Class Message Arguments The important thing, as always, is the group of message arguments. Here the message is msgNew, just as when we created the list object; because we are sending it to a different class, the message arguments are different. When sent to clsClass, msgNew takes a pointer to a CLASS_NEW structure. Like LIST_NEW, CLASS_NEW includes the arguments to OBJECT_NEW as part of its message arguments. Briefly, the CLASS_NEW message arguments are: • The same OBJECT_NEW arguments used by other objects-a lock, capabilities, a heap to use (and a UID field in which the Class Manager returns the UID of the object). • The method table (new.class.pMsg) which is where you tell the class which functions handle which messages. You must write the method table. This is the core of a class, and is discussed in great detail in the next section. • The ancestor of this class (new.class.ancestor). The Class Manager has to know what the class's ancestor is so that your class can inherit behavior from it, that is, let the ancestor class handle some messages. In this case, clsList is an immediate descendant of clsObject. • The size of the data needed by instances of the class (new.class.size). The Class Manager needs the information to know how much room to allocate in memory when it creates a new instance of this class. • The size of the structure that contains information used to create a a new instance of the class (new.dass.newArgsSize) 4.6.1 CHAPTER 4 I PENPOINT CLASS MANAGER 55 Creating a Class For a list, the instance data is just a pointer to the heap where it stores the list information, hence the size is (SIZEOF) sizeof(P_UNKNOWN). For other objects, the instance data may include a lot of things, such as window height and width, title, current font, etc. Note that an object has instance data for each of the classes it is an instance of-not just its immediate class, but that class's ancestor, and that ancestor's ancestor, and so on. P_UNKNOWN is the typedef used in PenPoint for a pOinter to an unknown type. ... ~ ~ Z ~ The instance data size must be a constant! If, say, a title string is associated with each instance of your class, then you need either to have a (small) fixed-size title or to keep the string separate and have a pointer to it in the instance data. Important! Instance data size must be a constant. Method Tables 4.6.2 Nearly all classes respond to messages differently than their ancestors do-otherwise, why create a new class? As a class implementer, you have to write methods to do whatever it is you want to accomplish (such as maintain a list, draw an icon, and so on) in response to a particular message. Some classes exist just to define a set of messages; the implementation of those messages is up to its descendants. In PenPoint, a method is a C function, called a message handler. The terms message handler and method are used interchangably. When a client sends a message to an instance of your class, you want the Class Manager to call the message handler that is appropriate for that message. You tell the Class Manager what to do with each message through a method table. A method table is simply a mapping that says "for message msgSomeMsg, call my message handler MyFunction." You specify the table as a C array in a file that is separate from your code (you must compile it with the method table compiler, described below). A method table file has the extension .TBL. Each class has its own method table; however, a single method table file can have method tables for several classes. At the end of the file is a class info table that maps a class to the method table for that class. There must be an entry in the class info table for each method table in the file. The file looks something like this: MSG_INFO clsYourClassMethods[] = { msgNewDefaults, "myClassNewDefaults" , msgSomeMsg, "MyFunction", flags, objCallAncestorBefore, 0, }i CLASS_INFO classInfo[] = { "clsYourClass", clsYourClassMethods, a 0, }i The quotation marks around the messages and classes are required. You can tell the Class Manager to call your ancestor class with the same message before or after calling your function by setting flags in the third field in the method table (the third field in the CLASS_INFO table is not currently used and should always contain 0). 1/1 1/1 S U 56 PENPOINT APPLICATION WRITING GUIDE """Identifying a Class's Message Table 4.6.:2.1 To convert the method table file into a form the Class Manager can use, you .compile the table file with the C compiler, then run the resulting object through the Method Table compiler (\PENPOINT\SDK\uTIL\CLSMGR\MT.EXE). This turns it into a .OB] file which you link into your application~ The most important argument you have to pass to msgNew when creating a class is a pointer to this method table (new.class.pMsg in the code fragment above). When you create the class, you set new.class.pMsg to clsYourClass. When an object is sent a message, the Class Manager looks in its class's method table to See if there is a method for that message. If not, the Class Manager looks in the class's ancestor's method table, and so on. If the Class Manager finds a method for the message, it transfers execution to the function named in the method table. When the Class Manager calls the function named in the method table, it passes the function several parameters: • The message sent (msg) . • The UID of the object that originally received the message (self) • The message arguments (pArgs). The Class Manager assumes that the message arguments are a pointer to a separate message arguments structure). • Internal context the Class Manager uses to keep track of classes (ctx) • A pointer to the instance data of the instance. ".. Self 4.6.3 Self is the UID of the object that received the message . . As we discussed before, when an object receives a message, the class manager first sees if the object's class can handle the message, then it passes the message to its ancestor, which passes the message to its ancestor, and so on. However, the data that each of those classes work on is the data in the object that first received the message (which is identified by self). This is fundamental to understanding object-oriented programming in PenPoint: calling ancestor makes more methods available to the data in an object, it doesn't add any new data. A second fundamental concept is that an ancestor may need to make a change to the data in the object. However, rather than making the change immediately by calling a function, the ancestor sends a message to self to make the change. Be careful not to get pulled into the semantic pit here, self means the object that received the original message, not the ancestor class handling the message. (Remember that the ancestors only make more functions available; not more data.) Because the message is sent to self, self s class can inspect the message and' choose whether it wants to override the message or allow its ancestor to handle it. Each ancestor inspects the message and can either override the message or pass it to its ancestor. This continues until the ancestor that sent the original message receives Of course, each ancestor deals with only the parts of the object data that it knows about; an ancestor can't modify a structure defined by its descendant. CHAPTER 4 / PEN POINT CLASS MANAGER 57 Creating a Class the message itself and, having given all of its decendents the opportunity to override the message, now handles the message itself {or even passes the message to its ancestor!). ... II.: Possible Responses to Messages " 4.6.4 c( Z c( . Here are some of the flavors of responses you can make to a message in a message handler: • Do something before and/or after passing it to the ancestor class. This might include modifYing the message arguments, sending self some other message, calling some routine, and so on. This means that the class will respond to the message differently than its ancestor. • Do something with the message, but don't pass the message to the ancestor class. This is appropriate if the message is one you defined, because it will be unknown to any ancestor classes. If the message is one defined by an ancestor, this response means that you're blocking inheritance, which is occasionally appropriate. • Do nothing but return some status value. This blocks inheritance, and means that it's up to descendant classes to implement the message. This is not as rare as it sounds; many classes send out advisory messages informing their instances or other objects that something has happened. For example, clsWindow sends self the message msgWinSized if a window changes size. This is useful for descendant classes which need to know about size changes, but clsWin itself doesn't care. What messages does your message handler have to respond to? It usually ought to respond to all the messages specific to your class which you define-no other ancestor class will! Ordinarily an instance of each class has its own data, so most classes intercept msgNew to execute a special initialization routine; if there are defaults for an instance's data, the class will also respond to msgNewDefaults. Most classes should also respond to msgFree to clean up when an instance is destroyed. Here is dsList's method table. II II Include files II fincludeI I where the msgs are. defined MSG INFO ListMethods [] { 1* clsObject methods *1 msgNewDefaults, "ListNewDefaults" , 0, msgInit, "ListInit", objCallAncestorBefore, msgFree, "ListMFree" , 0, msgSave, "ListSave", objCallAncestorBefore, msgRestore, "ListRestore", objCallAncestorBefore, ~ III III S U .... ~ When such a message is new to a class (no ancestor), it is called an abstract message. 58 PENPOINT APPLICATION WRITING GUIDE 1* clsList methods *1 msgListFree, "ListMFree", 0, msgListAddItem, "ListAddItem", 0, II FUnctions for the rest of clsList's messages . .. 0' ... ,' }; ° CLASS_INFO classInfo[] { "Li stMethodTable " , ListMethods, 0, }; ° Note that clsList responds to most intercepted messages by calling an appropriate fi!nction (Listlnit, ListMFree, and so on). The functions that implement the various list messages are not printed here; indeed, external code should never call routines internal to a class. One of the goals of object-oriented programming is to hide the implementation of a class from clients using the class. Chapter 5 I Developing an Application Thus far, we have described PenPoint and PenPoint applications from a conceptual point of view. By now you should understand how PenPoint differs from most other operating systems and what the PenPoint Application Framework and class manager do for you. With this chapter we start to address what you, as a PenPoint application developer, have to do when writing PenPoint applications. • The first section describes many of the things that you have to think about when designing an application. • The second section describes some of the things that you have to consider when designing an application for an international market. • The third section describes the functions and data structures that you will create when you write an application. • The fourth section describes the cycle of compiling, and linking that you will follow when developing an application. • The fifth section provides a checklist of things that you must do to ensure that your application is complete. • The sixth and following sections describe the coding standards and naming conventions used by GO. Included in these sections is a discussion of some of the debugging assistance provided by PenPoint. • The last section describes the tutorial programs provided with the SDK. ". Designing Your Application When you design a PenPoint application, there are several separate elements that you need to design: • The user interface • The classes • The messages • The message handlers • The program units This section points out some of the questions you must ask yourself when designing an application. This section does not attempt to answer any of the questions; many answers require a good deal of explanation, and many decisions involve your own needs. 5.1 60 PENPOINT APPLICATION WRITING GUIDE Just read this section and keep these questions in mind as you read the rest of the manual. ",. Designing the User Interface S.1.1 The most obvious part of a PenPoint application is the user interface. Almost as soon as you determine what your application will do, you should begin to consider your user interface. Your user application should be consistent with the PenPoint user interface, which is described in detail in the PenPoint User Interface Design· Guidelines. ",. Designing Classes 5.1.2 Pen Point provides a rich set of classes that can do much of the work for your application. Your task is to decide which of these classes will serve you best. The PenPoint Architectural Reference describes the PenPoint classes and what they can provide for you. If the classes provided by Pen Point don't do exactly what you need, you should look for the class that comes closest to your needs, then create your own class that inherits behavior from that class. Designing Messages 5.1.3 After determining that you need to create your own class, you need to decide what messages you need. Usually you add new messages to those already defined by your class's ancestors. However, the real trick to sub classing comes when you decide how to handle the messages provided by your class's ancestors. If you do not specify how your class will handle your ancestors' messages, the PenPoint class manager sends the messages to your immediate ancestor, automatically. If you decide to handle an ancestor message, you then need to decide when your ancestors handle the message, if at all. Do you: • Call the ancestor before you handle the message? • Call the ancestor after you handle the message? • Handle the message without passing it to your ancestor at all (thereby overriding ancestor behavior)? ",. Designing Message Handlers After determining the messages that you will handle, you then need to design the methods that will do the work for each of the messages. In considering the methods and the information they need, you will probably start to get an idea of the instance data that your class needs to maintain. 5.1.4 CHAPTER 5: DEVELOPING AN APPLICATION 61 Designing for Internationalization and Localization ~ Desigl11U!l'ilg ~licgli(ilm UlTIlafS1 5.1.5 When you understand the classes that you require, you should consider how to organize your classes and their methods into program units. The common approach used in our sample code is to place the source for each class into a separate file. You should consider whether a class will be used by a number of different applications or used by a single application. If the class can be used by more than one application (such as a calculator engine), you should compile and link it into a separate DLL (dynamic link library). Each application tells the installer which DLLs it needs at install time. The installer then determines whether the DLL is present or not. If not, it installs the DLL. Designing for Internationalization and Localization z o ~ u :::i II. II. C Z C e ~ 5.2 PenPoint 1.0 already includes many features that will be used to support internationalization. For example, PenPoint 1.0 uses PenPoint resource files to store its text strings; When localizing to a specific language, a different resource file will be created that contains text strings in that language. There are two aspects to the changes implied by PenPoint 2.0. The first is making your application port easily to PenPoint 2.0. The second is internationalizing your application. Pen Point 2.0 will incorporate some major changes that will cause applications compiled for PenPoint 1.0 to be incompatible with PenPoint 2.0. The data . created by 1.0 applications should still work under 2.0, and properly writtern 1.0 applications should be portable to PenPoint 2.0 with nothing more than a recompilation. This section describes how to write your PenPoint 1.0 application so that it will be portable to PenPoint 2.0. Using these guidelines does not mean that you will have internation~lized your application! Internationalization and Localization are much larger issues, and are dealt with elsewhere. These instructions are intended only to make it easier for you to port your American English application to PenPoint 2.0. The biggest change is that PenPoint 1.0 uses the ASCII character set, while PenPoint 2.0 uses Unicode. ASCII is an 8-bit character set; Unicode is a 16-bit character set. This affects character types, string routines, quoted strings, 9 > ICl ..... lit PenPoint 2.0 will contain support for applications that are written for more than one language or region. The process of generalizing an application so that it is suitable for use in more than one country is called internationalization. Modifying an application so that it is usable in a specific language or region is called localization. ,.,. Preparing for PenPoint 2.0 ~...... 5.2.1 62 PEN POINT APPLICATION WRITING GUIDE ".,. Character Types 5.2.1.1 PenPoint provides three character types: CHARS, CHAR16, and CHAR. The first two provide eight and sixteen bit characters, respectively. In PenPoint 1.0, the plain CHAR type is 8 bits long; in Pen Point 2.0, CHAR is 16 bits long. You need to convert all of your character data to use the CHAR type, except where you know the size you'll need will be the same under PenPoint 1.0 and PenPoint 2.0 (for example, in the code that saves and restores data). Any places where you depend on a CHAR having a small value, you should rethink the problem. For example, if you currently translate a character by indexing 256. .:element array (CHAR array [sizeof (CHAR) ]), you probably won't want to use the same strategy when sizeof (CHAR) , and therefore the size of your array, is 65,536. Any places where you depend on sizeof (CHAR) being one byte, you need to change the value. ".,. String Routines 5.2.1.2 All of the familiar C string ro,utines (strcmp, strcpy, and so on) will still exist in Pen Point 2.0, and they will still work on 8-bit characters. The INTL.H header file in PenPoint 1.0 defines a new set of string routines (named Ustrcmp, Ustrcpy, and so on) that perform the equivalent functions on 16-bit Unicode characters. In Pen Point 1.0, the U ... functions are identical to their 8-bit namesakes. In Pen Point 2.0, they will be true 16-bit routines. In other words, the old routines only work on CHARS strings, the U ... routines in 1.0 work on CHARS strings, in 2.0 the U ... routines will work on CHAR16 strings. If you use the U ... versions and CHAR strings now, you will riot have to change anything at 2.0. You should convert all your string routines to the U ... version wherever you are converting to CHAR strings. ".,. Character and String Constants When you use CHARS, you can use standard C conventions for forming character and string constants. That is: CHAR8 *s = "string"; CHAR8 c = ' c' ; When you use the CHAR16 type, you must preceed the character or string constant with the letter L, which tells the compiler you are using a 16-bit (or long) character, as in: CHAR16 *s = L"string" CHAR16 c = L' c' 5.2.1.3 CHAPTER 5: DEVELOPING AN APPLICATION Designing for Internationalization and Localization When you use the CHAR type, you must preceed the character or string constant with the identifier "U.,...L", which means UNICODE, long. In PenPoint 1.0, this tells the compiler to use 8-bit characters; in PenPoint 2.0, this tells the compiler to use 16-bit characters. CHAR *s = U_L"string"; CHAR c = U_L' c' ; ~ Debugging 5.2.1.4 DebugfO and related routines will continue to take ordinary 8-bit strings. This means that all debugging output will continue to be ASCII based. ".,. Versioning Data 5.2.1.5 Under PenPoint 2.0, you will still want yout application to be able to unfile any data it filed under 1.0. That is, although your users will have to install a new version of your product, you don't want them to have to throwaway anything they created with it! When you respond to msgRestore, check the filed version. If the version number is less than the defined value penpointRev2_0, read it into a structure that uses explicit CHARS where required, then copy it into your instance data. If the version number is greater than or equal to penpointRev2_0, your saving and restoring code should remain the same (and use CHAR types). ",. Preparing for Internationalization 5.2~2 PenPoint 1.0 does not contain all the messages, functions, and tools that you will need to internationalize your application. However, there are several facilities available in PenPoint 1.0 that you can use right now to reduce the work needed to internationalize. This section lists these facilities. ".,. Move Strings into Resource Files You should move as many of your text strings into resource files as possible. When text strings are hard-coded into your application, they are very difficult to translate and do not allow users to change language dynamically. However, if you move your application's text strings into resource files they are easy to translate and allow users to change language simply by substituting one resource file for another. If you use the StdMsg facility for displaying dialog boxes, error messages, and progress notes, your text strings are already in resource files. The positional parameter facility provided with StdMsg and the compose text string routines do not depend on the order of replaceable values in the function parameters. These functions are unlike printf, where the order of the function parameters is directly related to the order of replaceable values in the string. When you use StdMsg or compose text, the function parameters are always in the same order, 5.2.2.1 63 64 PENPOINT APPLICATION WRITING GUIDE but your string can use them in the order dictated by the national language in which you are writing. ,.,..,.. Identify and Modularize Code that Varies with Locale 5.2.2.2 When internationalizing an application, moving its text strings to resource files allows users to change the language, but in order to support another language, parts of your application code must be equally replaceable. For example, when sorting characters in another language, you must be prepared to handle different sort sequences. In 2.0, GO will provide a number of services to perform functions that vary by language. Under consideration are routines that provide sorting, number formatting, number scanning, numbers with units, times, and dates (input and output), character comparisons, character conversions, spell checking and so on. The PenPoint Services Architecture enables you to create functions that users can install and activate whenever they choose. For instance, users can install several different printer drivers, but they only make one driver current at a time. Similarly, users will be able to install several different sort engines and choose one to use with the current language. Right now, you can start to identify language-dependent routines, such as text manipulation, of your own. You can flag these routines and move them into separate modules. Part 13: Writing PenPoint Services in the PenPoint Architectural Reference describes how to create your own services. If you make your language-dependent functions into services in PenPoint 1.0, the change to 2.0 will be much easier. "". New Text Composition Routines 5.2.2.3 The file CMPSTEXT.Hcontains ComposeText routines for assembling a composite string out of other pieces. Use these routines to create strings in your UI - don't use sprintf! The ComposeText routines will also save you effort because you can specify the resId of a format string and the code will readit from the resfile for you. You can, of course, give the format string directly to the routines. ". Development Strategy Where do you start writing an application? The PenPoint Application Framework provides so much boilerplate work for you, it is very easy to create applications through incremental implementation. You start with an empty application, that is, one that allows the Application . Framework to provide default handling of most messages. Then, one by one, you add new objects and classes to the application, testing and debugging as you go. As we shall see in Chapter 6, the PenPoint SDK includes sources for an empty application, Empty App. You can copy, compile, install, and run Empty App. 5.3 CHAPTER 5: DEVELOPING AN APPLICATION 65 Development Strategy This section describes the fundamental parts of PenPoint applications. These are the parts that you will probably work on first. They are also the parts you will return to many times to modify. ",. Application Entry Point 5.3.1 All PenPoint applications must have a function named main, which is the entry point for an application. When the application is installed, main creates the application class and can create any other private classes required by all instances of the application. z o ~ u ::i a. a. C Z "'''' Application Instance Data 5.3.2 In PenPoint, objects that are instances of the same class share the same code. For example, if there are two insertion pads visible on the screen, they are both running same copy of the insertion pad class code, but each instance of the insertion pad has different instance data. AB soon as your application has data that can be different for each of its documents, your application needs to maintain instance data. What do you save in instance data? The most common use of instance data is to save identifiers for objects created by your application. The PenPoint object-data model suggests that any time you have data, you should use a class to maintain that data. When your application class has instance data, it must be prepared to respond to msgInit by initializing values in the instance data (if needed). ". Creating Stateful Obiects Stateful objects contain data that must be preserved when a document is not active. You can do some interesting things with an application that uses only the behavior provided by the Application Framework. However, soon after you start developing an application, you will want the application to be able to save and restore data when the user turns away from and turns back to its documents. To save and restore. documents, you need to create, save, and restore stateful objects. Usually an application's instance data contains some stateful objects and some non-stateful objects. If your application class has stateful objects, you must be prepared to handle: msgAppInit by creating and initializing the stateful objects required by a new document. Your application can create additional stateful objects later. msgSave by saving all stateful objects to a resource file. msgRestore by restoring all stateful objects from a resource file. Any class with instance data must respond to m5glnit in the· same way. 5.3.3 C C!) ~ 66 PEN POINT APPLICATION WRITING GUIDE Displaying on Screen Most applications need to display themselves on screen. The PenPoint Application Framework provides access to the screen by creating a frame object. When your application receives msgAppOpen, it should create the remaining non-stateful objects that it needs to display on screen, and then should display itself in the frame provided by the application framework. When your application receives msgAppClose, it should remove itself from the frame and destroy all of its non-stateful objects. Creating Component Classes If you create new component classes that can be shared by a number of different applications (or other components), you usually define the component classes in a DLL file. As an application executable file must have a function named main, a DLL file must have a function named DLLMain. DLLMaiil creates the component classes defined in the DLL. Development Cycles The compile, install, test, and debug cycle in PenPoint is similar to the development cycle for most other operating systems. This section briefly describes . the steps involved in the development cycle. Later sections cover these steps in greater detail. Compiling and Linking There are several types of files used to compile and link PenPoint applications. These files include: • The WATCOM make file • The application's method table files • The application's C source and header files • The linker command file for the application • The PenPoint SDK header and library files. Method Table Files You create a method table file to equate the messages handled by your class to a function defined in your source. You create one method table per class, but one method table file can contain several method tables. You compile the method table and then compile the resulting intermediate object file with the PenPoint method table compiler, MT. This produces: • A header file that you use when you compile your C source • An object file that you use when you link your application. CHAPHR 5: DEVELOPING AN APPLICATION 67 Development Cycles (: 5ClUIll'(1; I01ll'1ld He(t!ltcilel!' fD~eS 5.4.1.2 PenPoint applications are written in the C language; the object oriented extensions are provided through standard C function calls. The source for each class (application or component) is maintained in a separate file. I Following normal Cprogramming practice, it is advisable to define your symbols, structures, macros, and external declarations in one or more header (.H) files. Linker Command Files z o 5.4.1.3 a. a. PenPoint applications compiled for the Intel 80386 processor use the protected mode features of the chip. To produce a protected mode object file, you must link your application with the WATCOM OSl2linker. The OSl2linker requires additional commands that specifY internal names and memory requirements for the resulting executable (.EXE) or dynamic link library (.QLL) files. ct Z 0( "z II These commands are placed in the command files used by the linker. You can either build the command files separately or you can build the command files dynamically in your application's makefile. The sample applications in \PENPOINT\SDK\SAMPLE all build their linker command files dynamically. Pen Point SDK Files 5.4.1.4 The Pen Point SDK header and library files are in the directories \PENPOINT\SDK\INC and \PENPOINT\SDK\LIB, respectively. You should include these directories in your compiler and linker search paths. Installing the Application One difference between PenPoint and most other operating systems is that once you have compiled an application, you must install the application into PenPoint before you can use it. There is no "run" command in PenPoint, so you must use the Notebook to transfer control to the application. Additionally, all application code in Pen Point is shared. PenPoint must know where your application code is installed so that all instances of your application use the same code. There are two ways to install an application into PenPoint: • Install when you boot PenPoint • Install explicitly with the PenPoint application installer You can install an application when you boot PenPoint by adding your application's PenPoint name to your APP.INI file. You can explicitly install a PenPoint application by running the PenPoint application installer (found in the Connections and Settings notebooks). You can use the connections notebook to tell PenPoint to display the installable applications (or any other installable items) whenever a volume becomes available. ~ u :::; 5.4.2 68 PEN POINT APPLICATION WRITING GUIDE Debugging 5.4.3 There are a number of tools available to you to aid in debugging. Among them are: • Using Debugf or DPrintf statements to send text to the debugger stream. You can use a second monitor or the system log application to view the debugger stream. You can also save the debugger stream in a log file. The Debugf and DPrintf statements are described later in this Chapter. The system log application is described in PenPoint Development Tools. • Using the PenPoint source debugger (DB) to debug your application. The debugger is described in PenPoint Development Tools. • Handling msgDump. msgDump requests an object to format its instance data and send it to the debugger stream. While developing an application, you can send msgDump to any object whose state is questionable. From the PenPoint source debugger, you can use the od command to send msgDump to an object. It is not a good idea to send msgDump in production code. A Developer's Checklist 5.5 When your PenPoint application does what you want it to, you can stop and move on to your next project. However, PenPoint applications are far more useable when they can interact with the PenPoint operating system and other applications. There is such a wealth of interaction that it is easy to omit some behavior from your application. This section presents two checklists. The first checklist details all the interactions that you should include in your PenPoint application, starting at the fundamental Application Framework interactions. The second checklist lists the interactions that you should consider adding to your application to improve its appearance or usability. Checklist Of Required Interactions You should use this checklist to ensure that your application is complete. The items in the checklist point to parts of this manual and the PenPoint Architectural Reference where the item is described in detail. D Handle application class installation (in main when process Count equals 0) D create the application class D create any private classes used by the application class D Handle application object instantiation (in main when processCount is greater than 0) D create an instance of your class D create any private classes required by an instance of your application class D create any other objects required at the time 5.5.1 CHAPTER 5: DEVELOPING AN APPLICATION 69 A Developer's Checklist D Create and display windows o o D insert yourself into frame on msgAppOpen remove yourself from frame on rnsgAppClose. Handle application termination . .0 respond to rnsgFree protocol D Handle application deactivation or deinstallation (rnsgAppTerrninate). z o D D u :::i a. a. Handle rnsgSave o o D ~ Handle rnsgDurnp ""Z "" save data "!1!: save objects Handle rnsgRestore o o o [ IU o restore data' ...... 11'1 restore objects observe objects D Handle input o handle selection protocol D Respond to Printing messages ",. Checklist of Non-Essential Items Use this checklist to ensure that you have considered all possible non-essential additions to your application. The items in the checklist point to parts of this manual and the PenPoint Architectural Reference where the item is described in detail. D Add menus to SAMs D Handle Option sheet protocols o o 9 :: Create an option sheet Create application-specific option cards D Allow Application Embedding D Respond to move/copy protocol D Handle document import and export D Handle Undo D Respond to traversal protocols D Define document icons D Create Stationery D Create Help notebook files D Create Quick Help Resources 5.5.2 70 PENPOINT APPLICATION WRITING GUIDE GO's Coding Conventions 5.6 At GO, we have developed techniques to make PenPoint code easier to write, understand, debug, and port. Some of our techniques are stylistic conventions, such as how variable and function names should be capitalized. Others fall under the category of extensions to C, including a suite of basic data types that are compiler and architecture independent. This section describes: • The conventions that GO code follows • The global types, macros, constants, and constructions provided in PenPoint • Pen Point's global debugging macros and other functions that we have found useful to diagnose program errors. While we would be delighted for you to follow all of our conventions, we obviously do not expect every developer to do so. Conventions are a matter of taste, and you should follow a style that is comfortable to you. However, we do recommend that you make use of our extensions. They will help make your code easier to debug and port. Also, by describing our style, we hope to make it easier for you to understand our header files and sample code. Typedefs 5.6.1 All typedefs are CAPITALIZED and use the underscore character to separate words. typedef unsigned short typedef U16 U16i TBL_ROW_COUNTi Pointer types have the prefix P_. typedef unsigned short typedef TBL_ROW_COUNT U16, * P_U16i *P_TBL_ROW_COUNTi In structure definitions, the name of the structure type is also the structure tag. typedef struct LIST ENTRY U16 LIST ITEM positioni itemi LIST_ENTRY, *P_LIST_ENTRYi The tag name is used by the PenPoint Source-level Debugger. Variables 5.6.2 Variable names are mixed case, always starting with a lowercase letter, with capitialization used to distinguish words. Variable names do not normally include underscore characters. U16 numButtonsi Pointer variables are prefixed the name with a lowercase p. The letter following the p is capitalized. P U16 pCo1orMapi CHAPTER 5: DEVELOPING AN APPLICATION GO's Coding Conventions ~1Ul~~~~@~$ 71 5.6.3 Functions are mixed case, always starting with a capital letter, with capitialization used to distinguish words. Function names do not normally include underscore characters. Function names often use a Noun-Verb style. The verb is what the function does, the noun is ~he target of the function's action. z !;i o TilePopUp () ; PenStrokeRetrace(); u ::::i CI. CI. cC However, the main function is simply main. Z cC C) Defines (Macros and Constants) 5.6.4 Defines follow the same capitalization rules as variables and functions. Macros follow the rules for function names (mixed-case, first letter uppercase) and constants follow the rules for variable names (mixed-case, first letter lowercase). ~ Q ...... It'l #define Out Range (v, 1, h) ((v) < (1) II (v) > (h) ) *define maxNameLength 32 *define nameBufLength (maxNameLength+l) Class Manager Constants 5.6.5 You use several special kinds of constants when writing Class Manager code: • Class names • Well-known objects • Messages • Status values. (lass Names 5.6.5.1 Class names start with cls followed by the name of the class: clsList, clsScrollBar, and so on. ""'Well-Known Objects 5.6.5.2 Pre-existing objects in PenPoint to which you can send messages have the prefix "the": theRootWindow, theSystemPreferences, and so on. Messages Messages follow the standard style for constants, but have special prefix "msg". This is followed by the name of the class that defines the message (possibly abbreviated) and finally by the action requested by the message: msgListRemoveltem, msgAddrBookChanged, and so on. The exceptions to this rule are the basic clsObject messages, including msgNew, msgSave, and msgFree, which apply to all classes. These basic messages do not identify their class. ~ ~ 9... 5.6.5.3 72 PENPOINT APPLICATION WRITING GUIDE ".,. Status Values 5.6.5.4 Like messages, status values follow the standard style for constants. However, all status values start with the prefIx sts. This is followed by the name ofthe class that defInes the status value (possibly abbreviated) and fInally by a description of the status: stsListEmpty and stsListFull. For more information on the way unique messages and status values are constructed for a class, please refer to Part 1: Class Manager. ". Exported Names 5.6.6 At GO, we use prefIxes to indicate the architectural subsystem or component that defInes an exported variable, defIne, type, ~r function. PrefIxes help lower the possibility of name conflicts across PenPoint. They also help developers fInd which fIles contain the relevant source code. Note that fIelds within exported structures are not prefIxed, and locals within sample code source ftles are generally not prefIxed. either. For example, exported System Service names are all prefaced with as: fdefine osNumPriorities fdefine osDefau1tPriority typedef U16 51 0 os_INTE.RRUPT_ ID i STATUS EXPORTEDO OSProgramInstal1 P_CHAR pCommandLine, P_CHAR pWorkingDir, P_OS_PROG_HANDLE pProgHandle, P_CHAR pBaciName, P_CHAR pBadRef II II II II II II logical interrupt ID dlc or exe·name (and arguments) working dir of the program Out: program handle Out: If error, dll/exe that was bad Out: If error, reference that was bad )i The ftle \PENPOINT\SDK.\UTIL\TAGS\TAGS li~ts most of the exported names in PenPoint. You can scan it to see if a particular prefIx is used. The standard global include fIle \PENPOINT\SDK\INC\GO.H does not prefIx its identifIers-if something is common across PenPoint, such as the UI6 type, it is not prefIxed in any way. ". PenPoinl File Structure At GO, we follow a similar structure for both header ftles and source code fIles. The general structure of a header fIle is shown below: file header comments #includes fdefines typedefs global variables function prototypes message headers 5.7 CHAPTER 5: DEVELOPING AN APPLICATION 73 Pen Point File Structure Here is the general format of the source code file for a class implementation: file header comment hncludes ltdefines typedefs global variables internal functions exported functions 'methods" implementing messages class initialization function main function (in an app class file) ",. File Header Comment z o ~ u ::; A. A. 0( 5.7.1 ~ " [l The file header comment contains a brief description of the contents of the file. It also includes the revision number of the header file. If you have a problem using a PenPoint API, the revision level of the software is imp.ortant information. 9!. III 1:Q ..... 1ft ",. Includes 5.7.2 The include directives all follow the file header and are of the form: ltinclude Note that the filename for the include file does not contain any directory information. To locate include files, you specify an include path externally (either . in the INCLUDE system variable or as a compiler flag. "". Multiple Inclusion PenPolnt has many subsystems, each linked to other subsyst~ins. Each element tends to have its own header file(s). Consequently, including the header file for one subsystem leads to it including dozens of other subsystems. Often the same header files are included by other header files. This ·can slow down compiling and may lead to errors if header files are compiled in more than once. All PenPoint header files guard against being included multiple times by defining a unique string (FILENAME_INCLUDED) and checking to see if this string has been defined: /************************************************************ filename.h (el Copyright 1991,· GO Corporation, All Rights Reserved. Include file format. $Revision$ $Author$ $Date$ *************************************************************/ ltifndef FILENAME_INCLUDED ltdefine FILENAME INCLUDED 1/ defines, types, and so on of header file ltendif II FILENAME_INCLUDED where FILENAME is the name of the include file itself. 5.7.2.1 74 PEN POINT APPLICATION WRITING GUIDE You can speed up compiling by putting the same checks in your files to avoid even reading in a header file: tifndef LIST INCLUDED #include #endif II LIST_INCLUDED ~ Common Header Files 5.7.2.2 In a class implementation, if you include the header file of your immediate ancestor, this will usually include the header files of all your ancestors. If you include any header file at all, you will not need to include . ".. Defines, Types, Globals 5.7.3 This section of a file holds all of the fdefines, typedefs, and global and static declarations used only in this file. By grouping these items in one place, you will be able to find them more easily. ".. Function Prototypes 5.7.4 Function prototypes in header files indicate the parameters and format of PenPoint functions. Each is preceded by a comment header: 1***************************************************** *********************** Function returns TYPE Brief description. Comments, remarks. *1 function declaration; For example: 1***************************************************** *********************** OSHeapBlockSize returns STATUS Passes back the size of the heap block. 'The size of the heap block is the actual size of the block. This may be slightly larger than the requested size. See Also OSHeapBlockAlloc OSHeapBlockResize *1 STATUS EXPORTED OSHeapBlockSize ( P_UNKNOWN pHeapBlock, II pointer to the heap block P_SIZEOF pSize II Out: size of the heap block ); The header file descriptions of functions provide a "reminder" facility, not a tutorial; CHAPTER 5: DEVELOPING AN APPLICATION Pen Point File Structure ~ Message Headers 5.7.5 Many header files contain message headers, which are where messages are described and where their constants and related data structures are defined. Message headers have the following format: 1***************************************************** ************ msgXxxAction takes STRUC_TURE, returns STATUS category: message use Brief description. Comments, remarks. *1 *define msgXxxAction MakeMsg(clsXxxAction, 1) typedef struct STRUC_TURE { } STRUC_TURE, *P_STRUC_TURE; For example: 1***************************************************** *********************** msgAddrBookGetMetrics takes P_ADDR_BOOK_METRICS, returns STATUS. Passes back the metrics for tbe address book. *1 *define msgAddrBookGetMetrics MakeMsg(clsAddressBook, 8) typedef struct ADDR BOOK METRICS U16 numEntries; II Total number of entries U16 numServices; II Number of known services U16 numGroups; II Number of groups in the address book U32 spare1; U32 spare2; ADDR_BOOK_METRICS, *P_ADDR_BOOK_METRICS; We relied on the !egular format of message descriptions in header files to generate the datasheets for messages in the PenPoint API Reference. ~ In, Out, and In-Out 5.7.5.1 In a message header, you can assume that all parameters and message arguments are input-only (In) unless otherwise specified (Out or In-Out). ".. Indentation 5.7.6 Most PenPoint header files use four spaces per tab for indentation. Most programmer's editors allow you to adjust tab spacing; setting it to four will make it easier to read GO files. ".. Comments In general, slash-asterisk C comments (/* and */) indicate the start and end of functional areas, and slash C (/ /) comments are used for in-line comments within . ·functions. 5.7.7 75 76 PEN POINT APPLICATION WRITING GUIDE """,,' Some Coding Suggestions 5.7.8 Here are some of the other conventions that GO code follows (more or less). • Always include the default case in your switch statements to explicitly show that you are aware of what happens when the switch fails. • Don't use load-time initializations, except for constant values. Since PenPoint restarts code without reloading it, your code should explicitly initialize your variables. • Use 4I:defines for constants and put the defines in an include file (if it is used across multiple files) or at the beginning of the source file with a comment to indicate its use. • When defining an external function, use prototype declarations to describe the parameters and types it requires. • Make calls to external functions as specified by the include file of the subsystem exporting the function. • If your files fully declare the types of their functions, this will help them to be independent of any flags that may be set during compilation. • A source file should compile withoutwarnings. Pen Point code compiles without any warnings at warning level 3 using the Watcom C compiler. • Structure names must not be used as exported names. Use the type name to export a structure type. Structure names should he used only for self-referencing pointers. • Code for a single function should not exceed a few pages. Break it up (but don't go overboard!). • Use GO's Class Manager to support standard object-oriented programming methodologies. • The most importa~t parameter to a function should be the first parameter, for example, WindowDrag(pWin, newx, newy}. This is usually the object on which the function acts. ".. PenPoinl Types and Macros 5.8 In developing PenPoint, we found it useful to establish a "base" environment which goes beyond the structures and macros provided by the C language. This section describes many of these extensions. For a complete list, please look at \PENPOINT\SDK\INC\GO.H, where all of our extensions are defined. ".. Data Types To allow for portability between different C compilers and processors, we define six basic data types that directly indicate their 'size in bits. Three are signed: S8, SI6,and S32. The others are unsigned: U8, UI6, and U32. We also define corresponding pointers for each, prefixed with p _, and pointers to pointers, which are prefixed with PP_. 5.8.1 CHAPTER 5: DEVELOPING AN APPLICATION PenPoint Types and Macros To plan for internationalization efforts, we provide the CHAR data type. CHAR is functionally equivalent to char and is defined to be a us in Pen Point 1.0. In our 2.0 release, which will include support for international character sets, we will change CHAR to UI6. Simply stated, you should use CHAR instead of char to ensure an easier transition to PenPoint 2.0. CHAR has two related data types: P_CHAR, which represents a pointer to a character (char *), and PP_CHAR, which is a pointer to a string (char **). P_UNKNOWN is for uninterpreted pointers, that is, pointers that you do not dereference and about which code makes no assumptions. P _PROC is for pointers to functions. It assumes the Pascal calling convention. The SIZEOF type is for the sizes of C structures returned by sizeof. The status values returned by many functions are of type STATUS. This is a signed 32-bit value, although most subsystems encode status values to indicate the class defining the error to avoid status value conflicts. Section 2.8 describes status values in greater detail. ".. Basic Constants 5.8.2 Use the enumerated type BOOLEAN for logical values true and false. The BOOLEAN type also defines the values True, False, TRUE, and FALSE to preempt any discussion about capitalization rules. Similarly, null is the preferred spelling for null (0), but NULL is also defined. pNull is a null pointer. minS8,maxS8, minS16, maxS16, minS32, and maxS32 are the minimum and maximum integer values for the three signed types. maxU8, maxU16, and maxU32 are the maximum values of the three unsigned types. Obviously, the minimum unsigned value is zero. Names in many PenPoint subsystems can be no longer than 32 characters. This limit is defined as maxNameLength. Since strings are normally null-terminated, we define nameBufLength to be maxNameLength + 1. ".. Legibility 5.8.3 GO.H defines AND, OR, NOT, and MOD to be the corresponding C logical "punctuation;" this avoids confusion with the double-character bit operators and II. && ".. Compiler Isolation GO.H provides macros and other #defines that you can use to ensure compiler independence. 5.8.4 77 78 PENPOINT APPLICATION WRITING GUIDE ~ Function Qualifiers 5.8.4.1 GO.H introduces a layer in between the pascal, cdecl, and so on keywords of the Watcom C compiler by providing uppercase versions of all these keywords. Using the uppercase versions allow you to easily remove or redefine these keywords in source code if necessary. This allows you to experiment with changing the segmentation or calling sequences of your code to check for errors or changes. It's important to explicitly specifY calling conventions in your function prototypes so that code can compile with a different set of compiler switches from GO's defaults, yet still observe the protocol requirements. STATIC, LOCAL, and GLOBAL are compiler #defines that support the appearance (if not the reality) of modular programming. ' Instead of using these detailed qualifiers, you can use higher-level constructs. If you want to make a function available to other modules, EXPORTED says that it is a PASCAL function. There are other qualifiers that are only used by specialized kernel code or drivers. EXPORTEDO means that a function is a call gate. RINGCHELPER identifies functions which can be called by code at lower privilege levels. ~ Enumerated Values 5.8.4.2 Some compilers base the size of an enum value on the fields in that enum. This has unfortunate side effects if an enum is saved as instance data; programs compiled under different compilers might read or write different amounts of data, based on the size of the enum as they perceive it. To guarantee that an enum is a fixed size, use the Enum16 and Enum32 macros. These macros create enums that are 16 and 32 hits long, respectively. The macros expect a single argument-the name of the enum to be defined .. Within an Enum16 or Enum32, use the bit flags (flagO through flag31, also defined in GO.H) to define enumerated bits. Most PenPoint header files indicate when bits in an enum can be ORed to specifY several flags. If a PenPoint header file uses the flagO-style bit flags, assume that you can OR these flags. ".. Data Conversion/Checking Abs, Even, and Odd are macros that perform comparisons, returning a boolean. Max and Min return the larger and lesser of two numbers, respectively. OutRange and InRange check whether a value falls within a specified range. They work with any numeric data type. Be careful when using the Abs, Min, Max, OutRange, and InRange macros because their parameters are evaluated multiple times. If a function call is used as an argument, multiple calls to the function will be made to evaluate the macro. 5.8.5 CHAPTER 5: DEVELOPING AN APPLICATION 79 PenPoint Types and Macros ~i .. M~i'iipU~~~i@i'i 5.8.6 GO.H defines each bit as flagO through flag31, with flagO being the least-significant (rightmost) bit. LowU16, HighU16, LowU8, and HighU8 extract words and bytes by casting and logical shifts. MakeU16 and MakeU32 assemble words and 32-bit quantities out of 8-bit and 16-bit quantities. z o ~ FlagOn and FlagOff check whether a particular flag (bit) is set or reset. FlagSet and FlagClr set a particular flag. All four can take a combination of flags ORed together. You can use these bit manipulation macros with us, U16, or U32 data types. u ::::i AA- ce Z ce I) ~... ~ Tags 5.8.7 > There are several types of values passed around or otherwise shared among subsystems and applications in PenPoint: Q ...... 1ft • Class names • Messages • Return values • Window tags. All of these are 32-bit constants (U32). As you develop code and classes, you will define your own. It is vital that they not conflict, so GO provides a tag mechanism to guarantee unique names for them. GO administers a number space in which every developer can reserve a unique set of numbers. A tag is simply a 32-bit constant associated with an adminstered number. With each administered number you can define 256 different tags: because the administered numbers are unique, so will be the tags. You usually use your classes' administered number to define messages, status values, and window tags, since these are all usually associated with a particular class. See Part 1,' Class Manager for an explanation of how classes, tags, and administered numbers relate to each other. Return Values 5.S.S Most PenPoint code returns error and feedback information by returning special values from functions rather than generating exceptions. PenPoint still uses exceptions for certain types of errors: GP fault, divide by 0, and so on. Otherwise, functions that return success or failure must return a status value. Status values are 32-bit tags, defined in GO.H: typed~f S32 STATUS, * P_STATUSi The universal status value defined to mean "All is well" is stsOK. By convention, return values less than stsOK denote errors, while return values greater than stsOK indicate that the function did not fail, but may not have completed in the usual way. 9... 80 PENPOINT APPLICATION WRITING GUIDE There is a set of GO standard status values that you can use in different situations (described below), but usually each subsystem needs to define its own specific status values. To guarantee uniqueness among status values returned by third-party software, group your status values by class, even if the status does not come from a class-based component. GO administers well-known numbers for classes, as explained above in "Tags." Defining Status Values 1 GO.H defines a macro, MakeStatus(wkn,sts), to make a 32-biterror status value from a well-known 32-bit identifier and an error number. Usually, the well-known number is the class that defines the error. To make a status value that does not indicate an error, use MakeWarning(cls, msg), which creates a positive tag. So, if you want to define status values, all you need is a reserved class. GO can allocate one for you. You can then define up to 256 error status values and 255 success status values, using MakeStatus and MakeWarning with numbers in the range 0-255. If you need more status values, you can request another class UID. Pseudoclasses for Status Values 5.8.8.2 Since not everything in the PenPoint API is a message-based interface to an object-oriented class, there are several pseudoclasses defined solely to provide "classes" for status values from some subsystems: clsGO, clsOS, clsGoMath, and so on. You can ask GO for your own pseudoclasses for error codes if necessary. Testing Returned Status Values To test a STATUS value for the occurrence of an error, just test whether the value is less than stsOK. To test for one specific error, compare the value to the full error code from the appropriate header file. There are macros to assist in this, described in Section 3.11, "Error Handling Macros." There are a small number of system-wide error/status conditions. You can return a generic status value instead of defining your own, so long as you use it consistently with its definition. If you need to convey a slightly different sense, define your own context-specific status value. Here are the generic status values. Their" class" identifier is the pseudoclass stsGO. 5.8.8.3 CHAPTER 5: DEVELOPING AN APPLICATION 81 PenPoint Types and Macros Table 5-1 Generic Status Values Status Value Description stsOK Everything's fine. Errors stsBadParam One or more parameters to a function call or message are invalid. stsNoMatch A lookup function or message was unable to locate the desired item. stsEndOfData Reached the end of the data. u ::; stsFailed Generic failure. C stsTimeOut A time-out occurred before the requested operation completed. C C stsRequestNotSupported As it sounds. ii: stsReadOnly The target can't be modified. stsIncompatibleVersions The message has a different ve~sion than the recipient. ~ CI ...... stsNotYetlmplemented The message is not yet fully implemented. 11'1 stsOutOfMem The system has run out of memory. ~ D. D. Z z ...9 Non-Error Status Values stsRequestDenied The recipient decided not to perform the operation. stsRequestForward The recipient asks the caller to forward the request to some other object. stsT runcatedData The request was satisfied, but not all the expected data has been passed back. The macro StsOK returns true if the status returned by an expression is greater than or equal to stsOK. If you want to check for any status other than stsOK, use StsFailed. See "Error-Handling Macros," below. ",. Return Status Debugging Function 5.8.9 The function StsWarn evaluates any expression that returns a STATUS. If you do not set the DEBUG preprocessor variable during compilation, StsWarn is defined to be the expression itself.-a no-op. This means that whenever you call a function that returns a status value, you can use StsWarn. If DEBUG is defined, and the expression evaluates to an error (less than stsOK), then StsWarn prints the status value returned by the expression together with the file and line number where StsWarn was called (the special compiler keywords FILE . and LINE_). ".,. Human-Readable Status Values z o 5.8.9.1 You can load tables of symbol names in the Class Manager so that if you have set DEBUG, the above functions will print out a string for status return values, instead of a number. For an example of this, see the S_TIT.C file of the Tic-Tac-Toe sample program (\PENPOINT\SDK\SAMPLE\TTT), explained in the PenPoint Application Writing Guide. ---------- 82 PENPOINT APPLICATION WRITING GUIDE ". Error-Handling Macros 5.8.10 Every PenPoiht function or message returns a STATUS which you should check. . The following status macros make function checking much easier by handling typical approaches to handling errors. Table 5-2 Status Checking Macros Error Handling Approach Macro check for an error (no warning) StsChk check for an error and warn StsFailed return if result is an error StsRet jump to an error handler if result is an error StsJmp check that the result is not an error StsOK The Class Manager defines similar macros for checking the status values returned when sending a message. Each status value checker works with any expression that evaluates toa STATUS. Each takes the expression and a variable to assign the status to. All of these macros (except StsChk) call StsWarn, so that they print out a warning message if you set the DEBUG preprocessor variable during compilation. Since often one function calls another which also returns STATUS, using these macros consistently will give a "stack trace" indicating the site of the error and the nested set of functions which produced the error. The examples below assume that MyFuncO returns STATUS. StsChk(se, s) Checks for an error. Description Example Sets the STATUS s to the result of evaluating se. If s is less than stsOK, returns true, otherwise returns false. Does not print out a warning message. STATUS Si if (StsChk(MyFunc(paraml, param2), s)) ( II MyFunc() failed CHAPTER 5: DEVELOPING AN APPLICATION PenPoint Types and Macros 83 StsFaiied(se, s) Checks for an error. Description Example Sets the STATUS s to the result of evaluating se. If s is anything other than stsOK, returns true and prints an error if DEBUG is set. If sis stsOK, returns false. STATUS Si z if (StsFailed(MyFunc(param1, param2), s)) ( II MyFunc() returned other than stsOK, so check status switch (Cls(s)) { else ( II MyFunc() did the expected thing, so continue Remarks This is analogous to StsOK, but it reverses the sense of the test in order to be more consistent with other checking macros. o !iu :::i A. A. C( Z C( " ~ ~ ...9 ~ o ...... 11'1 StsJmp(se, s, labe/) Jump to label on error. Description Sets the STATUS s to se. If s is less than stsOK, it prints an error if DEBUG is set and does a goto to label. This is useful when you have a sequence of operations, any of which can fail, each having its own clean-up code. STATUS Si pMem1 = allocate some StsJmp (MyFunc (param1, pMem2 = allocate some StsJmp (MyFunc (param1, memorYi param2), s, Error1)i more memorYi param2), s, Error2)i return stsOKi Error2: II Handle error. 2. OSHeapBlockFree(pMem2)i Error1: II Handle error 1. OSHeapBlockFree(pMem1)i return Si 84 PENPOINT APPLICATION WRITING GUIDE StsOK(se, s) Checks that things are OK. Description Example Sets the STATUS s to the result of evaluating se. If s is greater than stsOK, returns true. Otherwise, prints an error if DEBUG is set and returns. STATUS s; if (StsOK(MyFunc(paraml, param2), s)) II MyFunc() succeeded, continue. else ( II MyFunc() failed, check status. switch (Cls(s)) ( Remarks This is analogous to StsFailed, but reverses the sense of the test and returns true for any status value that is not an error. In other words, this could return true but the status might be some other value than stsOK, such as stsNoMatch. StsRet(se, s) Returns status on error. Description Example Sets the STATUS s to se. If s is less than stsOK, prints an error if DEBUG is set and returns s. This is useful if one function calls another and should immediately fail if the second function fails. S'.!'ATUS s; II If MyFunc has problems, return. StsRet(MyFunc(paraml, param2), s); ". Debugging Assistance 5.9 GO has developed a set of useful functions and macros to assist in debugging PenPoint applications. They are no substitute for. DB, the PenPoint Source-level debugger, or the Pen Point mini-debugger (both these debuggers are documented in PenPoint Developement Tools). However, they help you trace the operation of a program without using a debugger. They are an elaboration of the time-honored technique of inserting printfs in your code. ",. Printing Debugging Strings DPrintf and Debugf print text to the debugger stream. They take a formatting string and optional parameters to display, in the same manner as as the standard C function printf. The only difference between DPrintf and Debugf is that Debugf supplies a trailing newline (if you want a newline at the end ofDPrintf. output, end it with \n). Debugf("Entering init method for clsApp"); Debugf(IImain: process count = %d", processCount); 5.9.1 CHAPTER 5: DEVELOPING AN APPLICATION 85 Debugging Assistance Debugge&' $1M'i'e~m 5.9.1.1 The debugger stream is a pseudo device to which programs (including PenPoint) can write debugging information. There are several ways to view the debugger stream. • If you have a single screen, you can see the most recent lines written to the debugger stream when you press I Pause I. z o !;i • If you have a second (monochrome) monitor, serial terminal, or PC running communications software, you can constantly watch the debugger stream on this monitor while you run PenPoint on the main (VGA) monitor. u ::::; A. A. '" "'z" Z • You can send the debugger stream to a log file, by setting the D debugger flag to the hexadecimal value 8000. Usually you do this in the ENVIRON.INI file, but you can also do it from the PenPoint symbolic debugger, or from the mini-debugger. It DebugSet=/DD8000 DebugLog=\\boot\tmp\run3.1og • You can use the System Log application to view the debugger stream while running a PenPoint appliction. None of these destinations are mutually exclusive. Assertions 5.9.:2 Often when working on functions called by other functions, you assume that the software is in a certain state. The ASSERT macro lets you state these assumptions, and if DEBUG is set, it checks to see that they are in fact the case. If they are not satisfied, it will print an error. For example, a square root function might rely on never being called with a negative number: void MySqRoot(int num) ( ASSERT(num >= 0, "MySqRoot: input parameter is negative!"); II Calculate square root ... The test is only performed if DEBUG is defined. Debug Flags At different times you want to print different debugging information, or you want your program to work a certain way. DEBUG is the common #define used by PenPoint to include debugging output; if you set DEBUG when compiling, the status-checking macros print out additional information, the ASSERT macro is enabled, and so on. You can use your own C preprocessor directives to get finer control over program behavior, for example OBJECT myDc #ifdef MYDEBUGl II Dump DC state ObjectCall(msgDump, myDc, Nil{P_ARGS)); #endif 5.9.3 86 PENPOINT APPLICATION WRITING GUIDE The disadvantage of this technique is that you must recompile your program to enable or disable this code. Another approach is to check the value of a flag in your code. PenPoint supports 256 global debugging flag sets. Each flag set is a 32-bit value, which means that you can assign at least 32-different meanings to each debugging flag set. Because there are 256 debug flags sets, they can be indexed by an 8-bit character. Commonly, we refer to a specific debugging flag set by the character that indexes that flag. GO has reserved all the uppercase character debug flags sets (A through Z), and has reserved some of the lowercase characters also. To find which debug flags set are available, see the file \PENPOINT\SDK\INC\DEBUG.H. You carr set the value of a flag set, and retrieve it. The typical way you use debugging flag sets is to set the value of a flag set before running a program, and in the program check to see which bits in the flag set are on. The function DbgFlagGet returns the state of a flag set ANDed with a mask. For example, if you were using the flag F in your program and were checking the third bit in it to see whether or not to dump an object, the code above would look like: if (DbgFlagGet(IF I , Ox0004)) { II Dump DC state ObjectCall(msgDumPI mydc , Nil(P_ARGS)); You only need to compile your program once, and you can turn on object dumping by changing the F flag set to Ox4 (or Ox8, or OxF004, and so on). The disadvantage of this is that the flag-testing code is compiled into your program, increasing its size slightly. Often programmers bracket the entire DbgFlagGet test inside an #ifdef DEBUGI#endif pair so that the flag-testing code is only compiled while in the testing version of their program. SeHing Debugging Flag Sets 5.9.3.1 There are several ways to set debugging flag sets. Note that there is a single set of these flags shared by all processes. . • In \PENPOINT\BOOT\ENVIRON.INI, set the flag to the desired bit pattern with: DebugSet=/DFnnnn IDfmmmm . "1 where F and fare letters that identifY a particular flag set and nnnn and mmmm are a hexadecimal values. For example, DebugSet=/DFE004. • By typing fs F nnnn in either the PenPoint source-level debugger or the PenPoint mini debugger. • By using DbgFlagSetO in a program, for example: DbgFlagSet(IF' IOxE004). CHAPTER 5: DEVELOPING AN APPLICATION Debugging Assistance $uggesiions 5.9.4 Isolate Debugging Messages 5.9.4.1 87 In general, always isolate all debugging code using hfdef DEBUG /* Debugging */ #endif z o ~ DEBUG is the conventional flag for debugging code, used by much of PenPoint. u ::::i Use the Status-Checking Macros 5.9.4.2 (!) Z ~ This status error listing shows the result ofsending msgDrwCtxSetWindow to objNull: C> ObjectCall: sts=stsBadObject "tttview.c".@232 task=Ox05d8 c> object=objNull c> msg=msgDrwCtxSetWindow, pArgs=26ec0438 » StatusWarn: sts=stsBadObject "tttview.c".@330 task=Ox05d8 » StatusWarn: sts=stsBadObject "tttview.c".@743 task=Ox05d8 Page fault in task 05D8 at lB:440CCD52. Error code = 0004. EAX=OOOOOOOO EBX=04000002 ECX=E002E5CF EDX=440CCD05 ESI=41BC8EFO EDI=4401EC38 EIP=440CCD52 EBP=004329EO ESP=004329CC FLG=00010246 CR2=0000000C CR3=00077000 CS=OOlB DS=002B SS=002B ES=002B FS=OOOO GS=OOOO TSS=05D8 TNAME=TICl the Debuggers If your code crashes unexpectedly, you can use the PenPoint mini-debugger to get a stack trace at the assembly-language level (type st at its> prompt). The linker's .MAP files enable you to translate assembly language addresses to functions and line numbers. If you suspect that your code is going to crash or behave improperly, run it from the Pen Point Source-level Debugger. This lets you step through your code, query and set values, and evaluate simple C expressions. Both debuggers are described in PenPoint Development Tools. Z c:t: Using the status-checking macros StsOK, StsJump, and so on, and their counterparts for sending messages may seem cumbersome, but they provide useful debugging information if DEBUG is defined. Also, since most functions and' message sends return the error status if they encounter an error, the "stack" of status prints provides a traceback showing where the error first occurred and who called it. ~Use a. a. c:t: 5.9.4.3 88 PENPOINTAPPLICATION WRITING GUIDE The Tutorial Programs Now that you've read the broad overview of Pen Point and its class-based applications, views, and objects, you are ready to get down to some of the nuts and bolts of writing an application. This section describes the remaining chapters in this book and the sample programs used in those chapters. The programs are: Empty Application Hello World (toolkit) Hello World (custom window) Counter Application Tic-T ac-T oe Template Application Chapter 6 explains how to compile and run programs using Empty Application. The chapter is quite long because it teaches the general development cycle: • How to compile an application • How to install an application on a PC or PenPoint computer • How to run an application • Some interesting things to look for when running any application • How to use some of the PenPoint debugging tools. The Empty Application is used to illustrate these steps, but the comments are applicable to all the other sample applications. Empty Application The tutorial starts off with an extremely simple application, Empty Application. Chapter 6 explains how to build and run it and how the application works. Empty Application has no view, no.data, and no application-specific behavior (apart from printing a debugging message). It only responds to one message from the Application Framework. However, it does create an application class (as all PenPoint applications must), and through inheritance from clsApp, you can create, open, float, zoom, close, rename, file, embed, and destroy Empty Application documents. CHAPTER 5: DEVELOPING AN APPLICATION The Tutorial Programs The next application is the traditional "Hello World" application. This prints Hello World! in its window. Rather than creating a window from scratch, this uses the existing User Interface Toolkit components. One of these is clsLabel, which displays a string. Hello World (toolkit) uses this existing class instead of creating its own. The components in the UI Toolkit are rich in features; for example, labels can scale their text to fit. If you can use a toolkit class, do so. 89 z o ~ u Hello World (toolkit) is described in Chapter 7. :::; II. II. c:( Z c:( (!) Hello World (Custom Window) ~ 5.1 Of course it is possible to draw text and graphics yourself. Hello World (custom window) draws the text Hello World in its window, and draws a stylized exclamation mark beside it. To do this, the application must create a separate window class and create a system drawing context to draw in its window, which is substantially harder than using toolkit components. Hello World (custom window) is described in Chapter 8. Counter Application Counter Application displays the value of a counter object in a label. It creates a separate counter class and interacts with it. The application has a menu created from UI Toolkit components which lets the user choose whether to display the counter value in decimal, hexadecimal, or octal. Both the application and the counter object must file state. The tutorial programs presented before Counter Application are not stateful, that is, they don't have data that the user can change permanendy. Realistic applications must allow users to . change things, so they must file their state. The application object uses a memory-mapped file to keep track of its state. Using a memory-mapped file avoids duplicating data in both the memory file system in program memory. By contrast, the counter object writes its value to a file when it is saved. The counter application is described in Chapter 9. 0.4 90 PEN POINT APPLICATION WRITING GUIDE ",.. Tic-Tac-Toe 5.10.5 The rest of the tutorial develops a "real" working application, Tic-Tac-Toe. This application is covered in Chapters 10 and 11. Tic-Tac-Toe presents a tic-tac-toeboard and lets the user write Xs and as on it. It is not a true computerized game-the user does not play tic-tac-toe against the computer. Instead, it assumes that that there are two users who want to play the game against each other. Although a tic-tac-toe game is not exactly a typical notebook application, Tic-Tac-Toe has many of the characteristics of a full-blown PenPoint application .. It has a graphical interface, handwritten input, keyboard input, gesture support, use of the notebook metaphor, versioning of filed data, selection, import/export, option cards, undo support, stationery, help text, and so on. ",.. Template Application 5.10.6 As its name implies, Template Application is a template, "cookie cutter" application. As such, it does not exhibit much functionality. However, it does handle many "typical" application messages. This aspect makes Template Application a good starting point for building a real application. ",.. Other Code Available Other source code is provided in the SDK in addition to the tutorial code. All the source to sample programs is on-disk in \PENPOINT\SDK\SAMpLE. Some of the other sample programs are described in Appendix A of this manual. Excerpts from sample programs also appear and are described in those parts of the Architectural Reference that cover related subsystems. 5.10.7 Chapter 6 / A Simple Application (Empty App) Applications written for many operating systems have to perform housekeeping functions by implementing their own boilerplate code, that is, code that is essentially the same from one application to the next. In PenPoint, the PenPoint Application Framework performs most of these housekeeping functions. By using the Application Framework, you can create an application that can be installed, that can create multiple instances of itself, that can handle page turns, floats and zooms, and that can display an option sheet, all without writing an additional line of code. Empty Application is a very simple application that does all of these things. The only additional code in Empty Application is a method that responds to msgDestroy by sending a message to the debug stream (when the program is compiled with the DEBUG preprocessor :fI:define name). The PenPoint Application Framework is responsible for everything else Empty Application does. Because the Application Framework handles so much of an application's interaction with the system, even such an insubstantial application has substantial functionality. ".. Files Used 6.1 The code for Empty Application is in \PENPOINT\SDK\SAMPLE\EMPTYAPP. There are three files in the directory: EMPTYAPP.C Contains the application class's code and initialization routine Contains the list of messages that the application class responds to and the associated message handlers to call METHODS.TBL' Contains the WATCOMMake file for EMPTYAPP. The make file also defines information required by the OS/2 linker. MAKEFILE ".. Not the Simplest The name Empty Application is not quite accurate, because it isn't totally empty. You could create an application with no method table at all, that is, one that responds .to no messages at all and relies entirely on clsApp to do the right thing. Empty Application handles one message by printing a string to the debug stream, so it needs a method table. 6.1.1 92 PENPOINT APPLICATION WRITING GUIDE ,.. Compiling and Linking the Code 6.2 The source code for sample applications is in subdirectories of \PENPOINT\SDK\SAMPLE. The subdirectory contains a "makefile" that tells WATCOM Make how to build the application. Thus to compile and link Empty Application, just change directory to \PENPOINT\SDK\SAMPLE\EMPTYAPP and typewmake. However, you do need to understand what the files are doing, so that you can later modify the makefiles to fit your needs. These sections describe the actual commands used to compile, link, and stamp EMPTYAPP. ", Compiling Method Tables 6.2.1 . You compile method tables into an object file by running them through the PenPoint method table compiler (in\PENPOINT\SDK\uTIL\CLSMGR\MT.EXE). By convention, method tables have the suffix .TBL. The control files used by WATCOM Make have a default rule for compiling a method table. MT produces an object file and a header file for the method table. You use these files when you compile and link the application. > > > > cd \penpoint\sdk\sample\emptyapp mkdir \penpoint\app\emptyapp set WCC386=/3s /Oif+ /s /W3 /We /Zc /Zq /fpc /zff /02 /En /OOEBUG \penpoint\sdk\util\clsmgr\mt methods.tbl -Fo=$methods.obj > $methods.err ", Compiling the Application ·6.2.2 To compile the application, use the W ATCOM C/386 compiler for protected-mode applicatiofols (WCC386P .EXE). > set WCC386=/3s /Oif+ /s /W3 /We /Zc /Zq /fpc /zff /02 /En /OOEBUG > wcc386p /Foemptyapp.obj emptyapp.c > emptyapp.err "'" Compiler and Linker Flags . The meanings of the various flags are listed in Table 6-1 (for more information, consult the WATCOM C/386 documentation): 6.2.2.1 Table 6-1 WATCOM Compiler and Linker Flags Flag Description 13s Generate 80386 instructions, pass arguments using stack. ID2 Include full symbolic debugging information IDDEBUG Create the preprocessor #define name DEBUG lEn Emit routine nam~ before prolog IFpc Generate calls to floating-point library IOif+ Optimization flags Is Remove stack overflow checks continued CHAPTER 6 I A· SIMPLE APPLICATION. (EMPTY APP) 93 Compiling and Linking the Code Table 6- 1 (continued) /W3 Set warning level to 3 /We Treat all warnings as errors /Zc Place literal strings in code segment Izff Makes the FS register floating (required for PenPoint) /Zq Suppress compiler informational messages. ~ Linking the Application 6.2.3 You link the application with the WATCOM OSl2linker for protected mode applications (WLINKP.EXE). Because PenPoint applications run in protected mode, you must tell the linker to produce a protected-mode executable file. The Makefile does this by creating a WLINK command file (the Make script deletes this command file before exiting). z o Caution This is only true for WLlNKP! If you have trouble compiling, make sure you are using the correct linker. The command file specifies what routines your code imports from DLL (dynamic link library) files, the memory model, privilege levels, access attributes, and protection. If you created the file by hand, it would contain: SYSTEM PenPoint NAME \386\PENPOINT\app\emptyapp\emptyapp.exe DEBUG ALL FILE METHODS.OBJ FILE EMPTYAPP.OBJ LIBRARY PENPOINT LIBRARY APP OPTION Quiet, Map=emptyapp.mpe, NOD, Verbose, Stack=15000, MODNAME=' GO-EMPTYAPP_EXE-Vl (0) , To tell the linker that it should get its options from a command file, precede the name of the command file with an at sign (@). In this case, which was drawn directly from WMAKE output, the command file is called EMPTYAPP.ELN. > wlinkp @emptyapp.eln MODNAME is the name that PenPoirit attaches to the process. It can be longer than the DOS eight-character file length limitation. In PenPoint, it contains important versioning information. It should be composed of your company's name followed by a dash, the project name followed by a dash, and a version string. The process name is not the application name. The process name only shows up in the debugger: the application name (the name of the directory in \PENPOINT\APP) appears in the Installer. If your application has a separate DLL, it will need its own command file. If the compile and link is successful, these commands create an EMPTYAPP.EXE file in \PENPOINT\APP\EMPTYAPP. ~ Stamping Your Application The last thing you must do in compiling an application is to give specific PenPoint attributes to the executable. To do this, you use the STAMP command, which creates an entry for your application in the PENPOINT.DIR file in the 6.2.4 ~ u ::::i II. II. cC ~ 94 PENPOINT APPLICATION WRITING GUIDE directory that contains the your application executable. You use STAMP to specify: • A long PenPoint file name for your application. Pen Point applications can have longer names than in DOS, just as PenPoint documents can have long names. • The PenPoint name of the application executable file. The .EXE name must match the directory name. Thus the second STAMP command below gives the file EMPlYAPP.EXE the PenPoint name Empty Application.EXE. • The version of PenPoint for which your application was compiled • A text string that describes the file for the Browser (APPLICATION, FONT, SERVICE, and so on) • A special identifier that tells the Installer that the file is an application (the value I 000 lAO) For EMPTYAPP, the stamp commands are: > \penpoint\sdk\util\dos\stamp \penpoint\app\emptyapp\ .. /g "Empty Application" /D emptyapp > \penpoirit\sdk\util\dos\stamp \penpoint\app\emptyapp /g "Empty Application.exe" /D emptyapp.exe > \penpoint\sdk\util\dos\stamp \penpoint\app\emptyapp\ .. /g "Empty Application" /a 01E00208 "1.0" > \penpoint\sdk\util\dos\stamp \penpoint\app\emptyapp\ .. /g "Empty Application" /a 0660013a "Application." > \penpoint\sdk\util\dos\stamp \penpoint\app\emptyapp\ .. /g "Empty Application" /a 0080013a 10001aO ,.-Installing and Running Empty Application 6.3 As described in the section "How Applications Work" in Chapter 3, you must install an application in Pen Point before you can run it. To install Empty Application, install it either at boot time or use the Application Installer on a running PenPoint system. The Application Installer is described in Using PenPoint. To install the application at boot time: • Add a line that says \\BOOT\PENPOINT\SDK\SAMPLE\Empty Application to \PENPOINT\BOOT\APP.INI. • Boot PenPoint on your Pc. • When the Notebook appears, draw a caret /\ in the TOC to insert an Empty Application document in the Notebook. When you create an Empty Application document in the Notebook, Pen Point creates a directory for the document in the application hierarchy (that's why it shows up in the table of contents), but it's only when you turn to the document's page that a process for the document is activated. Until then the document isn\ running and doesn't have a process or a valid clsEmptyApp object. The section "Installation and Activation," below, explains the difference between Installation and Activation, and the relationship between PenPoint processes and application classes. CHAPTER 6 / A SIMPLE APPLICATION (EMPTY APP) 05 Interesting Things You Can Do ~tm!~l1~!S!Btm~ !~Brm~f) ~~~ ~~~ ~~ 6.4 with Empty Application Although Empty Application doesn't perform any work, you can learn a lot about the operation of PenPoint by studying it. PenPoint provides a host of features and support to even the simplest application. You can try the following: ~ Create multiple instances (documents) of it. The PenPoint file system appends a number to each document to guarantee a unique application directory name in the application hierarchy. You create documents by performing one of these actions: • Choose Empty Application from the Create menu in the Notebook contents page. • Choose Empty Application from the pop-up menu that appears when you draw a caret /\ on the contents page. • Use the Stationery notebook to create Empty Application documents in the Notebook • Tap and hold on the title bar or name of an Empty Application document in the TOC to make a copy an existing document. Drag the icon that appears to where you want it to go, such as on the icon bookshelf, or elsewhere in the TOe. • Tap the Accessories icon in the bookshelf below the Notebook and tap the Empty Application icon in its window. • Float a Notebook Empty Application document by turning to the Notebook's table of contents and double-tapping on its page number (you must first enable floating in the Float & Zoom section of PenPoint Preferences). Compare the difference between an accessory and a floating document-accessories have no page number. • Zoom a floating Empty Application by flicking upwards on its title bar (you must first enable zooming in the Float & Zoom section of PenPoint Preferences) . • Display the properties of an Empty Application document by drawing a checkmark v' in its title bar. An option sheet for the document appears, with several cards in it for the document's appearance. z o !;i u ::::i a. a. oec II 96 PENPOINT APPLICATION WRITING GUIDE • In the table of contents, press and hold on a Empty Application title until a dashed line appears around it. You can now move the document around. Try moving it to another place in the Notebook. • Set the B debug flag to 800 hexadecimal in \PENPOINT\BOOT\ENVIRON.INI by adding or modifying a DebugSet line. As documented in \PENPOINT\SDK\ INC\DEBUG,H, this flag enables the connections notebook to display the contents of the file system. Boot PenPoint and start up the disk viewer by tapping on the connections notebook icon below the Notebook. Open up the boot volume by double-tapping on it. Select Directory on the View menu. By repeated double-tapping, open the \PENPOINT\Sys\Notebook directory. Its subdirectories should reflect the hierarchy of accessories and the hierarchy of documents and sections in the Notebook. • Give the Empty Application document a tab in the notebook by writing a "T" in its title bar. You can use the tab to navigate to the Empty Application document quickly. Ordinarily, you aren't able to open the active file system from the Disk Browser. This prevents users from accidentally modifying the application hierarchy. CHAPTER 6 / A SIMPLE APPLICATION (EMPTY APP) Code Run-Through • Give the Empty Application document a corkboard margin by writing a "C". in its title bar. A thick strip appears at the bottom of its window. • Before you install Empty Application, set the F debug flag to 1. You can either set this in \PENPOINT\BOOT\ENVIRON.INI by adding or modifying a line like DebugSet = IDF1, or by pressing I Pause I to go to the mini-debugger, entering fs F 1, then entering 9 to continue. When PenPoint initializes clsEmptyApp, clsEmptyApp checks this flag to see if it should trace messages. You can also use the System Log application to do this. • As you turn the pages, note the sequence of messages sent to each instance of clsEmptyApp by the PenPoint Application Framework. • On a PC, modify the VOLSEL line in ENVIRON.INI to use a hard disk. Run PenPoint, create a document, turn to it, then quit PenPoint. Look at the PenPoint Application Framework directory structure in \PENPOINT\SS\NK. • Select an Empty Application document in the table of contents, then use the disk viewer to open a directory on your hard disk. Copy the document to the hard disk. Then delete the document by drawing an X over it. • Set the G debugger flag to 1000 in \PENPOINT\BOOT\ENVIRON.INI (or set the flag with the fs mini-debugger command). This turns on debugging info for reading and writing resources in clsResFile. This is the class that files objects during msgAppSave processing. • Select an Empty App document in the TOC and move it by pressing and holding on its title. Move it inside another open document. If the other application supports it, the PenPoint Application Framework will embed the Empty Application document inside the other. • With the F1 debugging flag set, select an Empty App document in the TOC, then turn the page. Note how the document doesn't receive some messages. Now select something else, and see the Empty Application document receive the "missing" messages. Code Run-Through 6.5 Enough details of running Empty Application; now let's look at its C code. First we'll look at the layout of PenPoint source files. PenPoint Source Code File Organization Most source code in PenPoint has a similar structure. Although Empty Application is a very simple application, it has a similar layout to other applications. Remember that application programs have at least one class (the application class itself), so an application program is composed of at least these two files: • The method table that specifies the messages to which this class responds and the functions that handle those messages 6.5.1 97 98 PEN POINT APPLICATION WRITING GUIDE • The C source code for the class. The C source code for applications is usually organized in the following .way: • :ftdefines and typedefs • Message handlers • Class initialization routine • main entry point. The method table file always has the suffix .TBL. It looks like C code, but you process it with the method compiler MT before linking it into your program. Method Table File The method table file lists all the messages that the class handles. The PenPoint Class Manager sends any messages not listed in the method table to the class's ancestor for handling (and possibly to the ancestor's ancestor). Looking at a class's method table gives you a good feel for what the class does. A single method table file can have method tables for several different classes. The names of the method tables are usually pretty self-explanatory, for example, clsEmptyAppMethods is Empty Application's method table. You can still have one function that handles several different messages, by using the wild-card capabilities of method tables. Method table wild cards match any message within a given set of messages and call the associated method. Method table wild cards are described in Part 1: Class Manager of PenPoint Architectural Reference, Volume I Application C Code File The application's main routine is at the end of the source file. The operating system calls the application's main routine under two circumstances: • When installing the application (this happens only once) • When activating individual documents (this happens each time the user turns to or floats a document that uses the application). The C files for non-application classes don't have main routines, because only applications actually start C processes. The declaration for the main routine is: main (argc, argv, processCount) The argc and argv parameters are not used in PenPoint. PenPoint uses the processCount parameter to pass in the number of processes running this application. When processCount is 0, there are no other processes running this application; this indicates that PenPoint is installing the application. Once an application is installed, the process that has a process Count of 0 stays in memory until the application is deinstalled. On installation, main initializes the application class, by calling an initialization routine. This routine precedes main in the source file. Standard practice is to name this routine using the name of the application class (with an initial capital CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) Code Run·Through letter), followed by "Init". For example, the initialization routine for clsEmptyApp is ClsEmptyAppInit. When the initialization routine creates the application class, it specifies the method table used by the application class. In the method table, you establish a relationship between the messages that your class handles and the name of a function in your C code file that handles each message. These functions are called message handlers and are similar to the "methods" of other object-oriented systems. Message handlers should be local static routines that return STATUS. If your class does handle a message, the method table also indicates whether the Class Manager should call your class's ancestor before or after (if at all). ".,.. Message Handler Parameters 6.5.1.3 Because the Class Manager calls your message handlers, you don't get to choose message handler parameters. The arguments passed to all message handlers are: msg The message itself. self The object that received the message. pArgs The message argument. This 32-bit value can be either a single argument or a pointer to a structure containing a number of arguments. ctx A context maintained by the Class Manager. pData The instance data of self. Because the parameters to message handlers are always the same, \PENPOINT\SDK\INC\CLSMGR.H defines several macros to generate standard message handler functions, given only the function name (MsgHandler), or given the function name and types to cast its arguments to (msgHandlerWithTypes). At the beginning of an application source file are these items: . • hnclude directives for the header files required by the application • The internal routines used by your application's methods • Internal :/tdefines, and so on. "" Empty Application's Source Code 6.5.2 Here's an abstract of the Empty Application's C code and method table file: ".,.. Method Table 6.5.2.1 The method table file, METHODS.TBL, specifies that Empty Application has one message handler; clsEmptyApp handles msgDestroy in a function called EmptyAppDestroy. MSG_INFO clsEmptyAppMethods [1 = { #ifdef DEBUG msgDestroy, "EmptyAppDestroy", #endif o }; objCallAncestorAfter, 99 100 PENPOINT APPLICATION WRITING GUIDE The #ifdef and #endif statements cause the message handler to be defined only when you specify /DDEBUG in the compiler options. ,..,.,. C Source Code 6.5.2.2 There are three significant parts ofEMPTYAPP. c: • The main routine, which handles application installation and application startup • The initialization routine, which is invoked by main at installation time • The message handler for msgDestroy, which was specified in the method table. This section presents this code without further comment. Subsequent sections in this chapter examine the code in detail. The main routine for EMPTYAPP.C is: 1***************************************************** *********************** main· Main application entry point (as a PROCESS -- the app's MsgProc is where messages show up once an instance is running) . ****************************************************************************1 void CDECL main ( int char * U16 argc, argv[] , processCount) Dbg(Debugf("main: starting emptyapp.exe[%d]", processCount);) if (processCount == 0) ( II Create application class. ClsEmptyApplnit(); II Invoke app monitor to install this application. AppMonitorMain(clsEmptyApp,objNull); else II Create an application instance and dispatch messages. AppMain(); } II SUI'press compiler's "unused parameter" warnings Unused(argc); Unused(argv); 1* main *1 The initialization routine invoked by main on installation is: 1**************************************************************************** ClsEmptyApplnit Install the EmptyApp application class as a well-known UID. ****************************************************************************1 STATUS ClsEmptyApplnit (void) { CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) 101 Code Run-Through APP MGR NEW new; STATUS s; II II II Install the Empty App class as a descendant of clsApp. ObjCallRet(msgNewDefaults, clsAppMgr, &new, s); new.object.uid clsEmptyApp; new. object. key (OBJ_KEY)clsEmptyAppTable; new.cls.pMsg clsEmptyAppTable; new.cls.ancestor clsApp; II II II This class has no instance data, so its size is zero. new.cls.size II II II Nil(SIZEOF); This class has no msgNew arguments of its own. new.cls.newArgsSize = SizeOf(APP_NEW); new.appMgr.flags.accessory = true; strcpy (new. appMgr . company, "GO Corporation" )'; str,cpy (new. appMgr. defaultDocName, "Empty App Document"); ObjCallJmp(msgNew, clsAppM9r, &new, s, Error); II II II Turn on message tracing if flag is set. if (DbgFlagGet('F', OxlL» Debugf("Turning on message tracing for clsEmptyApp"); (void)ObjCaIIWarn(msgTrace, clsEmptyApp, (P_ARGS) true); return stsOK; Error: return s; } 1* ClsEmptyAppInit *1 Finally, the message handler for msgDestroy is: 1**************************************************************************** EmptyAppDestroy Respond to msgDestroy by printing a simple message if in DEBUG mode. ****************************************************************************1 MsgHandler(EmptyAppDestroy) ( Ufdef DEBUG Debugf("EmptyApp: app instance %p about to die!", self); fendif ' II II II II The Class Manager will pass the message onto the ancestor if we return a non-error status value. return stsOK; MsgHandlerParametersNoWarning; 1* EmptyAppDestroy *1 II suppress compiler warnings z o ~ u ::::; D. D. c:( '~ 102 PENPOINT APPLICATION WRITING GUIDE Libraries and Header Files 6.5.3 You interact with most of Pen Point by sending messages to objects. Thus a typical application only uses a few functions and only needs to be linked with APP.LIB and PENPOINT.LIB. However, you need to pick up the defInitions of all the messages you send, status values you check, and objects to which you send messages from their respective header files. Because Empty Application only looks for CLSMGR.H and APP.H messages, it only . needs to i~clude a few header files from \PENPOINT\SOK\INC: Table 6-2 Common Header Files File I'urt>ose GO.H Fundamental constants and utility macros in PenPoint. OS.H Operating System constants and macros. DEBUG.H Functions and macros to put debugging statements in your code. APP.H Messages defined by clsApp. APPMGR.H msgNew arguments of clsAppMgr used when an application class is created. CLSMGR.H Functions and macros that provide Pen Point's object oriented extensions to C. Class UID 6.5.4 To write even the simplest application you must create your own application class, so that's primarily what Empty Application does. Your application needs to have a well-known UID (Unique IDentifier, the "handle" on a Class Manager object) so the system can start it. All well-known UIDs contain a value that is administered by GO-this keeps them unique. When you finalize your application, you must contact GO Developer Technical Support for your own administered values. Until you register your application, you can use the predefined well-known UIDs that are set aside for testing. These test UIDs, wknGDTa through wknGDTg, are defined in \PENPOINT\SOK\ INC\uID.H for this purpose. Just define your class to be one of them: #define clsMyClass wknGDTa This is the approach that Empty Application takes. However, most other sample applications use well-known UIDs assigned to them by GO. Because most applications aren't part of the Pen Point API, these well-known UIDs don't show up in \PENPOINT\SOK\INC\UID.H. You can use local well-known UIDs instead of global well-known UIDs for classes that your application uses internally. These do not contain an administered value; however, you must ensure that they remain unique within your application. (One bit in the UID indicates whether it is local or global, another indicates whether it is well-known or private-making essentially 2 to the 30th possible local well-known UIDs.) CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) 103 Code Run-Through Be on the lookout for conflicts with other test software when using the well-known testing UIDs (wknGDTa through wknGDTg). If another application should use the same well-known testing.UID for one of its classes, you will have problems installing your application. Class Creation 6.5.5 The initialization routineClsEmptyApplnit creates the clsEmptyApp class. It also should look familiar to you from the discussion of classes in Chapter 3, Application Concepts. However, application classes are slightly different from other classes. You create most classes by sending msgNew to clsClass, whereas you create application classes by sending msgNew to clsAppMgr. z o -- STATUS !iu ClsEmptyAppInit (void) ::::i a. a. ( ABP_MGR_NEW new; STATUS 0( ~ Si II Install the Empty App class as a descendant of clsApp. II ObjCallRet(msgNewDefaults, clsAppMgr, &new, s); new.object.uid = clsEmptyApPi strcpy (new. appMgr.defaultDocName, "Empty App Document"); ObjCallJmp(msgNew, clsAppMgr, &new, s, Error); clsAppMgr Explained 6.5.5.1 The PenPoint Application Framework needs to know a lot of things about an application before it can set in motion the machinery to create an instance of the application. It needs to know • Whether the application supports embedding child applications • Whether the application saves its data or runs continuously ("hot mode") • Whether the application's documents appear as stationery or accessories • The icon to use for the application's documents. • The default name for the application's documents. Instances of the application class can't provide this information because the PenPoint Application Framework needs this information before it creates an application instance. To solve this cleanly, application classes are not instances of clsClass, but instead are instances of clsAppMgr, the application manager class. When an application is installed, its clsAppMgr instance is initialized, and this instance can supply the needed information. new.cls.newArgsSize = SizeOf(APP_NEW); new.appMgr.flags.accessory = true; strcpy (new.appMgr. company, "GO Corporation"); strcpy(new.appMgr.defaultDocName, "Empty App Document"); ObjCallJmp(msgNew, clsAppMgr, &new, s, Error); Tip Actually it is better to specify the default document name in the APP.RES file. This makes your application internationalizable. 104 PEN POINT APPLICATION WRITING GUIDE Application classes should be well known so that other processes can send messages to them. Otherwise, the Notebook would not be able to send messages t.Q your application class to create new documents when the user chooses it from the Create menu. You supply the UID for your application class in the msgNew arguments. ObjCaIIRet(msgNewDefauIts, clsAppMgr, &new, S)i new.object.uid = clsEmptyAppi new.object.key = (OBJJEY)clsEmptyAppTabIei new.cls.pMsg = clsEmptyAppTabIei new.cls.ancestor = cIsAPPi The key field is a way of protecting your application from accidental deinstallation. Only clients that know the key value that you used in the msgNew arguments will be able to deinstall your application. The dass.pMsg argument to msgNewestablishes the connection between the new class and its method table. More on this later. Documents, Accessories and Stationery 6.5.6 We have been referring to all copies of an application as documents. Not all documents in the system live on a page in the Notebook. Tools such as the clock and the personal dictionary float above the Notebook. If you set appMgr.flags.accessory to true, dsAppMgr will put your application in the Accessories palette. When the user taps on your application's document icon, dsApp will insert the new document on screen as a floating document. If you set appMgr.flags.stationery to true, dsAppMgr will put a blank instance of your application in the Stationery notebook (whether or not your application has custom stationery). When the user selects and copies the stationery document from the Stationery palette, dsApp will insert the new document in the Notebook. Tip For debugging purposes, it's convenient to be able to create documents both as floating accessories and Notebook pages. Where· Does the Application Class Come From? 6.6 The connection between a process running in PenPoint and an application class is not immediately obvious. You're probably wondering who calls the initialization routine for dsEmptyApp, who sends msgNew to create a new Empty Application instance, what process corresponds to this application instance, and why the familiar-looking C main routine doesn't do very much. ~ Installation and Activation The connection between an application class and a PenPoint process is an application's main routine. Every executable must have a main routine; it is the routine that PenPoint calls when it creates a new process running your application's executable image. 6.6.1 CHAPTER 6 / A SIMPLE APPLICATION (EMPTY APP) 105 Where Does the Application Class Come From? void CDECL main ( int char * U16 argc, argv[], processCount) Dbg(Debugf("main: starting emptyapp.exe[%d]", processCount);) The kernel keeps track of the number of processes running a particular program, and passes this to main as a parameter (processCount). For applications, there are two points at which PenPoint executes does this: application installation and document activation. Application installation occurs when the user or APP.INI installs the application, that is, when PenPoint loads the application from disk into memory. No application documents are active at this point, but the code is present on the PenPoint computer. z o ~ u :i Do Do C Document activation occurs every time the user starts up a document that uses the application, typically by turning to its page. ~ When the user creates a document in the Notebook's TOe, PenPoint does not execute the application code, it merely creates a directory for the document in the application hierarchy. Try it: while turned to the TOe, create a new Empty Application document. The DebugfO statement in main does not print out anything until you turn to the document. In MS-DOS, loading and executing code are part of the same operation; on a PenPoint computer, installing an application, creating documents for that application, and executing application code are three separate operations. On MS-DOS, quitting an application is an action under the control of the user. In PenPoint, when the user turns away from a document, PenPoint determines whether it should destroy the application process or not. PenPoint does not keep running processes around for every application on every page, so it destroys processes that aren't active (thereby destroying application objects). PenPoint starts and destroys application processes without the user's knowledge and, ideally, without any effect apparent to the user. ".,.. A Simple Discussion of .main When an application is installed, PenPoint creates a process and calls the application's main to run in the process. At this time, this is the only copy of the application running on the machine; thus, processCount contains the value o. During installation, you should create your application class and any other classes you need. You then call AppMonitorMain, which handles application installation, import, copying stationery and resources, and so on. Empty Application doesn't take explicit advantage of any of these features, but other programs do. 6.6.1.1 106 PEN POINT APPLICATION WRITING GUIDE if (processCount == 0) { II Create application class. ClsEmptyAppInit()i II Invoke app monitor to install this application. AppMonitorMain(clsEmptyApp, objNull)i else { The process that Pen Point created at application installation keeps on running until PenPoint deactivates or deinstalls the application. Therefore, all subsequent processes that run the application's code will have processCount values greater than o. When a document is activated (typically by the user turning to its page), Pen Point calls main (processCount is greater than zero). At this point you should call the PenPoint Application Framework routine AppMain. This creates an instance of your application class and starts dispatching messages to it (and other objects created by the application) so that the new instance can receive Class Manager messages: if (processCount 0) { else II Create an application instance and dispatch messages. AppMain()i 1* main */ Most applications follow these simple steps and have a main routine similar to the one in EMPTYAPP.C. ,.,.,... A Complex Explanation of main The following paragraphs explain the process interactions taking place around main. Read on if you really want to understand how application start-up works. Installation occurs when PenPoint reads \PENPOINT\BOOT\APP.INI (and SYSAPP.INI) and when the user installs applications using the Installer. PenPoint or the Installer calls the System Services routine OSProgramInstall, which loads the executable code for your application (EMPTYAPP.EXE) into a special area of PenPoint memory called the loader database. OSProgramInstall also creates a new PenPoint process and calls the function main with process Count equal to o. At this point your code should initialize any information that all instances will need, such as its application class and any other non-system classes required by your application. The one thing every Empty Application instance needs is clsEmptyApp itself, hence when the main routine in EMPTYAPP.C is called with processCount of 0, it creates clsEmptyApp. 6.6.1.2 CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) 107 Handling a Message ~ Application Instanation The process that PenPoint creates when processCount equals 0 also manages other application functions that are not specific to an individual document. These functions include copying stationery during installation, de-installation, file import, and so on. Rather than saddle your application with all these responsibilities, the PenPoint Application Framework provides a class, clsAppMonitor, which provides the correct default behavior for all these functions. When you call AppMonitorMain it creates one of these objects and dispatches messages to it. If your application needs to do more sophisticated installation (shared dictionaries, configuration, and so on), or can support file import, you can subclass clsAppMonitor and have a custom application installation manager. Activation occurs when the user chooses Empty Application from the Tools notebook or the Stationery notebook, but in a roundabout fashion. The Notebook or bookshelf application sends msgAppCreateChild to the current selection. When clsApp receiyes this message, it creates a newslot in the application hierarchy for the new document. But a process and an application object aren't created until needed. The document may not be activated until the user turns to the document's page, or otherwise needs to interact with it. Activating an Application At or before the point where a live application instance is needed, the PenPoint Application Framework sends the application's parent msgAppActivateChild. While processing this, clsApp calls the System Services routine OSProgramInstantiate. OSProgramInstantiate creates a new PenPoint process, and in thecontext of that process it calls the function main with processCount set to a non-zero number. Finally there is a running process for an Empty Application document! In theory, you could put any code you want in main, just like a vanilla C program. However, the only way a PenPoint application knows what to do-when to initialize, when it's about to go on-screen, when to file, etc.-is by messages sent to its application object. So, the first and only thing you need to do in main when processCount is non-zero is to create an instance of your application class and then go into a dispatch loop to rec:eive messages. This is what. the AppMain call does. From here on until the user turns away from the document and the application instance can be terminated, AppMain does not return. Handling a Message clsEmptyApp only responds to one message. That doesn't mean that Empty Application documents don't receive messages-if you turned on tracing while running Empty Application, you'll have seen the dozens of messages that an Empty Application application instance receives during a page turn. It means only that clsEmptyApp lets its ancestor take care of all messages, and it turns out that clsApp does an excellent job of handling PenPoint Application Framework messages. 1.3 z o ~ u ::. a. a. '" [ 108 PENPOINT APPLICATION WRITING GUIDE A real application or other class has to intercept some messages, otherwise it has the same behavior as its parent class. In the case of an application class, the application needs to respond to PenPoint Application Framework messages that tell documents when to start up, when to restore themselves from the file system, when they are about to go on-screen, and so on. If the application has standard application menus (SAMs), it will receive messages such as msgAppPrint, msgAppPrintSetup, and msgAppAbout, from the buttons in the menus. Often, the class responds to these messages by creating, destroying, or filing other objects used by the application. EMPTYAPP.C doesn't do any of this; all it does is print a string when it receives one particular message, msgDestroy. Method Table 6.7.1 Objects of your classes (especially application instances) receive lots of messages regardless of whether or not you want your class to deal with those messages. Your class' method table tells the Class Manager which messages your class intercepts. This code sample is from Empty Application's method table file (METHODS.TBL): *ifndef CLSMGR INCLUDED *include *endif MSG_INFO clsEmptyAppMethods [] = { hfdef DEBUG msgDestroy, "EmptyAppDestroy", *endif objCallAncestorAfter, o }; CLASS_INFO classInfo[] = "clsEmptyAppTable", clsEmptyAppMethods, 0, o }; This basically says "If an instance of dsEmptyApp receives msgDestroy, call EmptyAppDestroy, then pass the message to dsEmptyApp's ancestor." The link between the functions in a method table and a particular class is established by one of the msgNew arguments when you create the class (new.dass.pMsg). This is the name you associate with the class's MSG_INFO array in the CLASS_INFO array; in this example, the pMsg is dsEmptyAppTable. This code sample is from ClsEmptyAppInit in EMPTYAPP.C: II Install the Empty App class as a descendant of II (msgNewDefaults, nevi. object. uid nei'l. object. key new.cls.pMsg new.cls.ancestor CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) Message Handler 'Y msgDe$h'(l)'Y 109 6.1.2 The names of most messages identifY the class that defined them: for example, msgAppOpen is defined by clsApp. Messages defined by the Class Manager itself are the exception to this convention. msgDestroy is defined by the Class Manager in \PENPOINT\SDK\INC\CLSMGR.H; this is why Empty Application's METHODS.TBL #includes this header file. The Class Manager responds to msgDestroy by destroying the object that received msgDestroy. The Class Manager actually turns around and sends the object another message, m5~Free, to free the object. Message Handler 6.8 The message handler (also known as method) is just a C routine you write that does something in response to the message. Empty Application's message handler for msgDestroy is EmptyAppDestroy, which just prints a string to the debugger stream. The name you give the message handler must match the name you specified in the method table (EmptyAppDestroy). ,..,.. Parameters 6.8.1 The parameters that the Class Manager passes to a message handler are: msg The message received by the instance self The UID of the instance that received the message pArgs The message arguments passed along with the message by the sender of the message ctx A context that helps the Class Manager keep track of the class in the instance's hierarchy that is currently processing the message pData A pointer to the instance data, information specific to the instance whose format is defined by the class. Here's the definition from CLSMGR.H: II Definition of a pointer to a method. typedef STATUS (CDECL * P_MSG_HANDLER) ( MESSAGE msg, OBJECT self, pArgs, P ARGS CONTEXT ctx, P IDATA pData ); You never call your message handlers, the Class Manager does, and always with the same set of parameters. The PenPoint Method Table Compiler generates a header file containing function prototypes for all the message handlers specified in the message table; you can guard against accidentally leaving out a parameter by including these files in your class implementation C files: Uncl ude #include #include #include tinclude II for debugging statements. 1/ for application messages (and clsmgr.h) II for AppMgr startup stuff II for strcpy(). II method function prototypes generated by MT 110 PENPOINT APPLICATION WRITING GUIDE MsgHandler is a macro that expands into the correct definition of a pointer to a message handler. It saves you typing all these parameters. 1***************************************************** *********************** EmptyAppDestroy Respond to msgDestroy by printing a simple message if in DEBUG mode. ****************************************************************************/ MsgHandler(EmptyAppDestroy) { ",. Parameters in EmptyAppDestroy 6.8.2 It turns out that Empty Application's EmptyAppDestroy routine doesn't need most of the parameters. The informative string prints out the UID of self (the Empty Application document that received the message) and doesn't use the rest of the parameters. ltifdef DEBUG Debugf("EmptyApp: app instance %p about to die!", self); ltendif We aren't interested in the msg, since the Class Manager should only call this function with msgDestroy. clsEmptyApp has no instance data, so we don't need pData. (Remember, we specified that class. size is 0 when we created clsEmptyApp.) Although we don't need these parameters, there is no way to tell the class manager not to send them. The C compiler will warn about unused parameters in functions. Since many message handlers won't use all their parameters, CLSMGR.H defines a fragment of code, MsgHandlerParametersNoWarning, which mentions each parameter. You can stick this in your message handler at any point. MsgHandlerParametersNoWarning; II suppress compiler warnings } /* EmptyAppDestroy *1 "., Status Return Value 6.8.3 Message handlers are supposed to return a status value. This is important both to indicate to the sender of the message that the message was handled successfully, and to control how the Class Manager passes the message up the class ancestry chain. Empty Application's method table directed the Class Manager to pass msgDestroy to clsEmptyApp's ancestor after calling Empty Application's handler: msgDestroy, "EmptyAppDestroy", objCallAncestorAfter, If EmptyAppDestroy were to return an error status value, the Class Manager would not call·the ancestor, and the normal result of sending msgDestroy would be pre-empted (the application object would not go away). Sometimes this is what you want, but not in this case, so we return stsOK. /1 The Class Manager will pass the message onto the ancestor 1/ if we return a non-error status value. return stsOK; CHAPTER 6 I A SIMPLE APPLICATION (EMPTY APP) 111 The Debugger Stream ?r Mess(lge H(li'id~ei"s ~i'e ~i'yv~~e 6.8.4 Although message handlers are just regular C functions, you normally do not want other code to call your message handlers. One of the goals of object-oriented programming is to hide the implementation of functionality from clients of that functionality. Clients should communicate with your objects by sending them messages, not by calling your functions. That way you can change the names and implementation of a message handler without affecting clients of your API. ,.. Using Debug Stream Output 6.9 There are two main ways to debug programs in PenPoint: • Send data to the debugger stream z o ~ • Use the PenPoint Source-Level Debugger u ::::; AA- Additionally, you can use the PenPoint mini-debugger, which is part of Pen Point, but is most useful when debugging kernel and device-interface code. "' ~ Note that you can't use standard DOS-type debugging tools (such as WATCOM Debugger or Microsoft CodeView) because these packages require your executable file to be runnable under DOS. This section discusses sending data to the debugger stream. For a complete tutorial on how to use the PenPoint Source-Level Debugger (DB) and mini-debugger (mini-DB), see the part on debugging in PenPoint Development Tools. ,.. The Debugger Stream 6.10 You can send data to the debugger stream with Debugf and DPrintf statements in your code. This is much like debugging a DOS application by adding printf statements to the code. . EMPTYAPP.C uses the system debugging output function Debugf to print strings to the debug stream (Empty Application doesn't use its PenPoint windows to display anything). Debugfis much like the standard C function printf. The %p formatting code in the format string means "print this out as a 32-bit hexadecimal pointer." Because UIDs such as self are 32 bits, this is a quick and dirty way to print aUlD value. The Class Managerdefines routines that convert UIDs to more meaningful values which this application could have used instead; the message tracing and status warning debugging facilities use these fancier output formats. Seeing Debug Output There are several ways to view the information sent to the debugger stream: • If you press I Pause I while running PenPoint, your screen will switch from graphics to text display and you will see strings that have been written to the debugger stream. 6.10.1 112 PENPOINT APPLICATION WRITING GUIDE • If you have a second monitor and do not set monodebug=off in your MIL.INI file, debugger stream data is displayed on the second monitor. • If you turn on the 8000 bit in the D debug flag, debugging strings will be copied to the file \PENPOINT.LOG on theBootVolume (the volume specified . with VOLSEL in ENVIRON.INI). • You cari run the System Log application. The System Log application is a Pen Point application that allows you to review data sent to the debugger stream. To use it, install it by un commenting it in SYSAPP.INI or by installing from disk (just as you install a~y other application in PenPoint). When the System Log application is installed, it adds its icon to the Accessories window. Tap on the icon to open the application. Debug strings appear in the System Log application. You can scroll up and down to see its contents. You can also check flags, see available memory, and set flags from the System Log application. To learn more about the System Log application, see the Part on debugging in PenPoint Developer Tools. Chapter 7 / Creating Obiecls (Hello World: Toolkit) Although Empty App shows that the Application Framework can do many things for an application, Empty App is still rather boring, in that it doesn't contain anything or show anything on screen. This chapter describes how to create objects. It so happens that these objects also display things on screen. A standard, simple test program is one that prints "Hello World." In PenPoint, there ar~ two different ways to approach this: • Vse PenPoint's VI Toolkit to create a standard label that contains the text. • Create a window and draw text in it using text and dra~ing services provided by the ImagePoint imaging model. These two styles mirror two general classes of program. Programs such as database programs and forms can use standard user interface components to create dialogs with the user. Programs such as presentation packages and graphics editors do a lot of their own drawing. They need to create a special kind of window and draw in it. This chapter shows the first approach; the application clsHelloWorld calls on the VI Toolkit to create a label object. The next chapter describes how to create a window and draw in it (and also discusses how to create a new class). Even programs that do use custom windows will make heavy use of the VI Toolkit. Every application has a menu bar with standard menu buttons, a frame, and at least one option sheet, and most programs will add to these to implement other controls and dialogs with the user. An application can choose not to use these, but doing so involves extra work and goes against· GO's User Interface guidelines. Moreover, using the VI Toolkit is much simpler than using a window. The toolkit component classes are all descendants of clsWin, the class which supports overlapping windows on the screen (and printer). But they know how and when to draw themselves and file. themselves, so there's very little you need to do besides create them and put them in your application's frame. ~Hello'K Hello World (toolkit) uses VI Toolkit components to display the words "Hello World!" These components know how to draw themselves and position themselves. Consequently, it's extremely' simple to create the application. The directory \PENPOINT\SOK\SAMPLE\HELLOTK actually contains two different versions of Hello World (toolkit). The first version, HELLOTK1.C, creates a single label in its frame. Vsually you want to put several windows in a frame; this is more complex and is handled by HELLOTK2.C. 7.1 114 PENPOINT APPLICATION WRITING GUIDE Compiling and Installing the Application 7.1.1 Both versions of Hello World (toolkit) (HELLOTK1.C and HELLOTKl.C) have a single C file. Consequently, compiling, downloading, and running it are the same as for Empty Application. Because there are multiple versions of the code, copy the version you want to run to HELLOTK.C before running WMAKE: > copy hellotkl.c hellotk.c > wmake This creates a \PENPOINT\APP\HELLOTK directory and compiles a HELLOTK.EXE file in it. It uses STAMP to give the directory the long name "Hello World (toolkit)" and the .EXE the long name "Hello World (toolkit).exe." Install Hello World (toolkit) either by adding \\BOOT\PENPOINT\APP\Hello World (toolkit) to \PENPOINT\BOOT\APP.INI before starting PenPoint or by installing the application using the Installer. Create Hello World (toolkit) application instances from the Stationery notebook, from the stationery quick menu, or from the Accessory palette. Interesting Things You Can Do with HelloTK Alas, Hello World (toolkit) doesn't do much more than Empty Application besides display a label. It doesn't do anything less, so you can create multiple instances of it as accessories or as pages in the Notebook, you can trace messages to it (by setting the F flag to Ox2 0), and so on. The only new thing to do is to notice how the label draws itself. Try zooming or resizing a Hello World (toolkit) document. Code Run-Through for HELLOTKI.C HELLOTKl.C creates a single label in its frame. Highlights of HELLOTK 1 The method table for Hello World (toolkit) only responds to one message, msgAppInit. msgApplnit, "HelloApplnit", objCallAncestorBefore, In order to avoid clashing with other Hello World applications, HELLOTKl.C uses a different testing well-known UID. #define clsHelloWorld wknGDTb II avoids clashing with other HelloWorlds Most of the work is done in the message handler HelloAppInit, which responds to msgAppInit by creating the client window (a label). So that it can use the same method table as HELLOTK2.C, HELLOTKl.C responds t() msgAppOpen and msgAppClose as well as msgAppInit; however, it does nothing with these messages but return stsOK. CHAPTER 7 I CREATING OBJECTS (HELLO WORLD: TOOLKIT) Code Run-Through for HELLOTK 1.C The only significant thing that happens in HelloWorld (toolkit) is that it responds to msgApplnit by creating a label. The code to do this is very simple, about 35 lines, but deciding what to do in those few lines introduces several key . concepts in PenPoint application development: • Choosing what classes to use + Deciding when to create objects. It also involves some common programming techniques: • Creating an instance of a class • Sending messages to self. ,..,.. Sending Messages 7.2.2 Empty Application receives messages, but does not send messages. Often in responding to a message, your application must send other messages. It might send messages to other objects, or even send itself messages to get its ancestor classes to do things. Hello World (toolkit) shows how to send a few simple messages. ~,. ObiectCall 7.2.2.1 Use ObjectCall to pass a message to another object in your process. This works like a function call: the thread of control in your application's process continues in the message handler of the other object's class, and returns to your code when the other object's class returns a status value to your code. There are other ways to send a message: • Asynchronously • Using the input queue • Between processes. In a simple application, stick to ObjectCall. ~ Testing Return Values and Debugging 7.2.2.2 Because messages return a status value, you should usually check their return values. This would ordinarily lead to lots and lots of constructs such as the following in your code: if ((s = ObjectCall(msgXXx, someObject, &args) / / Print standard warning if DEBUG set / / Handle error . .. < stsOK) { To save typing and code complexity, for every Class Manager function that returns a status value, there are macro versions of the function that jump to an error handler, or return true if there's an error, etc. For ObjectCall, these are ObjCallWarn, ObjCallRet, ObjCalljmp, ObjCallChk, and ObjCaliOK. 115 116 PENPOINT APPLICATION WRITING GUIDE ObjCallWarn's value is the status value returned by ObjectCall. If compiled with the DEBUG flag set, then ObjCallWarn prints out an error string if the status value is an error (that is, less than stsOK). The other macros incorporate ObjCallWarn into their behavior: ObjCallRet calls ObjCallWarn and then returns the status value if it is an error ObjCallJmp calls ObjCallWarn and then jumps to a error label (where you can handle the error) if the status value is an error ObjCallChk calls ObjCallWarn and then returns the value true if the status value is an error ObjCallOK calls ObjCallWarn and then returns the value true if the status value is not an error (that is, greater than or equal to stsOK). Creating Toolkit Components HELLOTKl.C responds to msgAppInit by creating a label. Labels are one of the many components provided by the DI Toolkit. But why does it create this particular kind of component? What Kind of Component? It's worth taking a close look at the class hierarchy poster to see all the toolkit classes. Most of the DI Toolkit classes are windows. There's only one class in the system that knows how to do windows (multiple overlapping regions on a pixel device), and that's clsWin. But it's the descendants of clsWin that know how to draw something interesting in themselves. The toolkit components inherit from clsBorder, a special kind of window which knows how to draw a border. You'll find the toolkit classes "under" clsBorder in the class list, class diagram, and class browser. Part 4: UJ Toolkit of the PenPoint Architectural Reference explains the DI Toolkit in all its multi-level glory. For a hint of what it can do, here's a screen shot indicating all the different kinds of DI Toolkit components present: \ Tip Some of the key decisions you make in any object-oriented programming system are choosing what built-in classes to use and which built-in classes to subclass. CHAPTER 7 I CREATING OBJECTS (HELLO WORLD: TOOLKIT) ',7 Code Run-Through for HELLOTK1.C figure 7-1 UI Toolkit Components Page Number Frame Title Bar Menu Bar Pull-down Menu Menu Button Tab Bars III .... Vertical Scrollbar ......u Option Sheet 0 l1li Option Table ~ ...lie ~ u Labels Shadow Popup Choice Toggle Table Command Bar Bookshelf· There are many other classes in the VI Toolkit. There are several base classes that provide lower-level functionality. And there are many specialized components classes, such as date handwriting input fields. For Hello World (toolkit), all we peed is a class that can display a string, such as clsLabel. ....... ... 118 PENPOINT APPLICATION WRITING GU.IDE To learn more about a class, you can try to • Use the class browser to get a brief description of it and all its messages • Read about it in its subsystem's Part of the PenPoint Architectural Reference • Look up its "datasheets" in the PenPoint API Reference • Look at its header file in \PENPOINT\SDKIINC. The class browser, the header, and the documentation all give you the information you need to create an instance of the class. msgNew Arguments for clsLabel As you learned in Chapter 3, PenPoint Application Concepts, you create objects by sending msgNew to their class. Different classes allow different kinds of initialization, so you pass different arguments to different classes. The documentation states what message arguments a given class needs for msgNew. In the header file the information is expressed as follows: msgNew takes P_LABEL_NEW, returns STATUS This says that you should pass in a pointer to a LABEL_NEW structure when you send msgNew to clsLabel. What you typically do is declare a LABEL_NEW structure in the routine which sends msgNew. You can give this any variable name you want; Hello World (toolkit) calls it In (the first letter of each part of the structure name). At the top of HelloAppInit: ***** /* ******* ************** the client windovi (a label) . ********* LABEL NEW STATUS **************/ In; Before you send msgNew to a class, you must always send msgNewDefaults to that class. This takes the same message arguments as msgNew (a pointer to a LABEL_NEW structure in this case). This gives the class and its ancestors a chance to initialize the structure to the appropriate default values. It saves your code from initializing the dozens of fields in a _NEW structure. II Create the Hello label window. ObjCaIIWarn(msgNewDefaults, clsLabel, &In); Now you're ready to give values to those fields in the structure which you care about. Figuring out what's in a _NEW structure is not easy. It contains initialization information for the class you are sending it to, along with initialization information for that class's ancestor, and for its ancestor's ancestor, all the way to initialization arguments for clsObject. Sometimes the only initializations you're interested in are the ones for the class you've chosen, but in Tip If you have a programmable editor, you can use tags to quickly jump to structure definitions. See \PENPOINT\SDK\ UTIL\TAGS\TAGS.DOC for more information. CHAPTER 7 / CREATING OBJECTS (HELLO WORLD: TOOLKIT) Code Run-Through for HELLOTK1.C 110 the case of the UI Toolkit, you often have to reach back and initialize fields for several of the ancestor classes as well. In.label.style.scaleUnits In. label. style.xAlignment In. label. style.yAlignment In.label.pString = bsUnitsFitWindowProper; = lsAlignCenter; = = lsAlignCenter; "Hello World!"; You can look up the hierarchy for a class by looking in the API Reference for that class. The description of the _NEW structure for msgNew always gives the _NEW_ONLY structures that make up the _NEW structure. Thus, the hierarchy for clsLabel expands to: LABEL_NEW ( OBJECT NEW ONLY WIN NEW ONLY GWIN NEW ONLY EMBEDDED- WIN- NEW- ONLY BORDER NEW ONLY CONTROL NEW ONLY LABEL NEW ONLY object; win; gWin; embeddedWin; border; control; label; '" ...... tU III When in doubt, rely on msgNewDefaults to set up the appropriate initialization, and modify as little as possible. All you need do to create a label is pass clsLabel a pointer to a string to give the string a label. However, the LABEL_STYLE structure contains various style fields which also let you change the way the label looks. We want the text to fill the entire window, so the scaleUnits field looks promising. This is a bit field in LABEL_STYLE, but rather than hard-code numeric values for these in your code, LABEL.H defines the possible values it can take. One of these is IsScaleFitWindowProper. This tells clsLabel to paint the label so that it fills the window, but keeping the horizontal and vertical scaling the same. Other style fields control the alignment of the text string within the label. In this example, we'd like to center the label. By the way, one reason that clsLabel has so many style settings and other msgNew arguments is that many other toolkit components use it to draw their text, either by creating lots of labels or by inheriting from clsLabeL Thus clsLabel draws the text in tab bars, infields, in notes, and so on: 1/ Create the Hello label window. ObjCallRet(msgNewDefaults, clsLabel, &In, s); In.label.style.scaleUnits = bsUnitsFitWindowProper; In.label.stYle.xAlignment lsAlignCenter; In. label. style.yAlignment = lsAlignCenter; In.label.pString = "Hello World!"; ObjCallRet(msgNew, clsLabel, &In, s); Now the label window object exists. The Class Manager passes back its UID in In. object. uid. But at this point it doesn't have a parent, so it won't ever show up on-screen. o ~ 120 PENPOINT APPLICATION WRITING GUIDE ".. Where the Window Goes 1.2.4 Empty Application appeared on-screen even though it didn't create any windows itself. The Application Framework creates a frame for a document. Frames are a UI Toolkit component. A frame can include other windows. Empty Application's frame has a title bar, page number, and resize boxes; you've seen other applications whose frames also include tab bars, command bars, and menu bars. Most importantly, a frame can contain a client window, the large central area in a frame. Empty Application didn't supply a client window (hence it looked pretty dull). Hello World (toolkit) wants the label it creates to be the client window. The messflge msgFrameSetClientWin sets a frame's client window. But the label must have its frame's UID to send a message to its frame. Hello World (toolkit) didn't create the frame, its ancestor clsApp did. clsApp does not define a message to get the main window. Instead, it provides a message to get diverse information about application instances, including the main window of that application. (An application can have a different main window for itself other than a frame. Information made public about instances of a class is often called metrics, and the message to get this information for an application is msgAppGetMetrics. msgAppGetMetrics takes a pointer to an APP_METRICS structure, one of the fields in the structure is mainWin. Here is how HelloAppInit gets its main window: APP METRICS ami II Get the app's main window (its frame). ObjCallJmp(msgAppGetMetrics, self, &am, s, error); II Insert the label in the frame as its client window. ObjCallJmp(msgFrameSetClientWin, am.mainWin, \ (P_ARGS)ln.object.uid, s, error); Note that the code sends msgAppGetMetrics to self. We have been talking loosely about Hello World (toolkit) doing this and that, but remember that this code is run as a result of an instance of clsHelloWorld receiving a message, and that clsHelloWorld is a descendant of clsApp. Thus the document is the application object to which we want to send msgAppGetMetrics. In the middle of responding to one message (msgApplnit), we need to send a message to the same object which received the message. This is actually very common. The Class Manager provides a parameter to methods, self, which identifies the object which received the message. ".. Why msgApplnit? Earlier you turned on message tracing to Empry Application~ What this does is cause the class manager to dump out every message received by instances of clsEmptyApp. You should have noticed that each Empty Application document receives dozens of messages during the course of a page turn to or from itself. 1.2.5 CHAPTER 7 / CREATING OBJECTS (HELLO WORLD: TOOLKIT) Code Run·Through for HELLOTK1.C 121 These messages are sent to documents (application instances) by the PenPoint Application Framework. If you want your application to do something, you must figure out when to do it. Your process can't take over the machine and do whatever it wants, it must do what it wants in response to the appropriate messages. One of the hardest things in PenPoint programming is figuring out when to do things. So, when should Hello World (toolkit) create its label? Because it inserts the label in its frame (using msgFrameSetClientWin), it can't create the label before it has a frame. But it should have a label in its frame before it goes on screen. It turns out that clsApp creates the document's frame in response to msgAppInit. Thus Hello World (toolkit) can get its frame and insert the label in its msgAppInit handler, but it must do so after clsApp has responded to the message. This is why its method table tells the Class Manager to first send the message to its ancestor: III l- V .., III MSG INFO clsHelloMethods [] msgAppInit, msgAppOpen, l1li o "HelloAppInit", objCallAncestorBefore, Note that doing this relies on knowing what the ancestor class does. You'll spend a lot of time reading Part 2: Application Framework of the PenPoint Architectural Reference to learn about the PenPoint Application Framework messages and how clsApp responds to them. II Why Did the Window Appear? If you're familiar with other window systems, you may be wondering how the label gets sized, positioned, and made visible on screen. These will be explained during the development of other tutorial programs. But here's a summary~ When the application is about to go on screen it receives msgAppOpen. clsApp inserts the main window (the frame) in the Notebook's window and tells it to lay out. clsFrame takes care of sizing and positioning its title bar, page number, move box, and client window (the label). Each of these windows is sent a message by the window system to repaint itself when it is exposed on screen. clsLabel responds to the repaint message by painting its label string. Thus all you need to do is put a toolkit window inside your frame, and the system takes care of the rest for you. Possible Enhancements . You can change the class of the window created in HelloAppInit to be some other kind of window class by changing the class to which Hello World (toolkit) sends msgNewDefaults and msgNew. But different classes take different message arguments when they are created. You need to replace the declaration of a LABEL_NEW structure with the msgNew arguments of the new class. Warning Passing the wrong message arguments with a message is one of the more common errors in PenPoint programming. The C compiler will not catch the error. 122 PENPOINT APPLICATION WRITING GUIDE If the class handling the message expects different arguments, it will blindly read past the end of the structure you passed it, and if it passes back values, it will overwrite random memory. A given class receiving a given messsage has to be given a pointer to the appropriate structure, otherwise unpredictable results will occur: but it can't enforce this. There are many classes which inherit from clsLabel, consequently, if you used one of these, you wouldn't even have to change the initialization of the structure. For example, clsField inherits from clsLabel, and FIELD_NEW includes the same NEW_ONLY structures as LABEL_NEW, so it takes the same border and label specifications. ".. Highlights of the Second HelloTK 7.3 HELLOTK2.C is much like HELLOTKl;c'·Thebig difference is that it supports more than one window. Most applications have many windows within their frame. You compile and run it the same way. Just copy HELLOTK2.C to HELLOTK.C and follow the steps outlined above. ". Only One Client Window per Frame 7.3.1 Frames only support a single client window. But usually you'll want several windows in your application. You have two alternatives: • Subclass clsFrame (which is very difficult) • Create a client window that is another window, then insert all the windows you want into that client window (which is quite easy). The toolkit provides two window classes that help you organize the windows within the client window. These are called layout windows. To understand why they're needed, you need to know a little bit about layout. ". Layout When you're using several windows, something is responsible for positioning them on the screen. You can set a window's position and size to some value with msgWinDelta. However, if the user changes the system font size, or resizes the frame, or changes from portrait to landscape mode, the numbers you pick are unlikely to still be appropriate. It's more convenient to specifY window locations at an abstract level: • "I want this window below that one, and extending to the edge of that other one. " • "Position these windows in two columns of equal width." The DI toolkit provides two layout classes which support these styles, clsCustomLayout and clsTableLayout. Both are packed with features. Both lay out their ~wn child windows according to the constraints (for custom layout) or algorithm (for table layout) which you specifY. The general way of using layout windows is to create one, specifY the layout you want, and insert the windows in it. 7.3.2 CHAPTER 7 I CREATING OBJECTS (HELLO WORLD: TOOLKIT) 123 Highlights of the Second HelloTK HELLOTK2.C uses a custom layout window and positions a single label.in its center using CIAlign (clCenter , clSameAs, clCenter). II Specify how the custom layout window should position the label. CstmLayoutSpeclnit(&(cs.metrics)); cs.child = In.object.uid; cs.metrics.x.constraint = C1Align(clCenterEdge, clSameAs, clCenterEdge); cs.metrics.y.constraint = C1Align(clCenterEdge, clSameAs, clCenterEdge); cs.metrics.w.constraint = clAsIs; cs.metrics.h.constraint = clAsIs; ObjCallJmp(msgCstmLayoutSetChildSpec, cn.object.uid, &cs, s, error2)i ".. Possible Enhancements 7.3.3 You might consider trying to add one of these to HELLOTK2.C: ".,.. Fields 7.3.3.1 Change the label to be an editable field. There are several ways of handling handwriting in PenPoint. One way is to use a UI component which allows editing, clsField. Since fields have similar behavior to labels (they display a string, have a length, font, and so on), clsField inherits from clsLabei. This makes it easy to update the application: replace the lABEL_NEW structure with FIELD_NEW, and clsLabel with clsField, and recompile. You can now hand-write into the field. ".,.. More Components ;!! ....., u III o 7.3.3.2 Add some more controls, using different custom layout constraints. You should be able to put together a simple control panel. ".,.. General Model of Controls You specify the metrics of each control when you create it, then you insert them in your layout window. The controls lay themselves out, repaint themselves, and support user interaction without any intervention on your part. When the user activates a control, the control sends its client (set in the msgNewarguments of clsControl, or by msgControlSetClient) a notification message. For more information on controls, see Part 4: UJ Toolkit of the PenPoint Architectural Reference. 7.3.3.3 II Chapter 8 / Creating A New Class· (Hello World: Custom Window) This chapter describes how you create a new class. Along the way, the chapter also describes how you display the string "Hello World!" on screen by creating and .drawing in custom windows. ,.. Hello World (Custom Window) 8.1 Hello World (custom Window) creates an instance of a custom window and uses the custom window to display some text. It's still not a very- realistic application because it doesn't me any data, but it does use an additional class, a descendant of c1sWin, to do its drawing. Your application may be able to use only standard DI components from the DI Toolkit and other PenPoint subsystems; but if not, you will be creating your own window class to draw stuff. So far our example applications have been quite simple and have not needed to define their own classes (apart from creating a subclass of clsApp). One of the big advantages in object oriented programming is that when you do define a class, other applications can create instances of the class (rather than defining new classes on their own). So that other applications can use the new class, developers often define each class in a single C file and then compile and link one or more C mes into a DLL. The C me that contains the application class (and has main) is compiled into an executable file. To show this coding style, Hello World {custom Window) is implemented as an application and a separate DLL. There are two parts to Hello World (custom Window), c1sHelloWorld (the application class), ana c1sHelloWin (the window class). HELLO.C implements c1sHelloWorld and HELLOWIN.C defines c1sHelloWin. HELTBL.TBL contains the method table for clsHelloWorld; HELWTBL.TBL contains the method table for clsHelloWin. ". Compiling the Code Compiling and linking the Hello World (custom Window) executable is somewhat similar to compiling Empty Application. However, Hello World (custom Window) is compiled and linked in two parts: an EXE me that contains the application, and a DLL file that contains the window class (c1sHelloWin). You can build Hello World (custom Window) by changing directory to \PENPOINT\SDK\SAMPLE\HELLO and typing wmake. 8.1.1 126 PENPOINT APPLICATION WRITING GUIDE Note that because the application class, clsHelloWorld, and its window class are in different files, compiling is more efficient if they have separate method table files (HELTBL.TBL and HELWTBL.TBL). linking DLLs When you link DLLs (dynamic link libraries), the information you provide the linker is slightly different from the information you provide when linking an executable image. If you use the makefile to build Hello World (custom window), the file builds the command file for you. If you created the command file by hand, it would contain: SYSTEM PenPoint DLL NAME \386\PENPOINT\app\hello\hello.dll DEBUG ALL FILE helwtbl.obj FILE dllinit.obj FILE hellowin.obj EXPORT=dll LIBRARY penpoint LIBRARY win SEGMENT CLASS 'CODE' Share, CLASS 'DATA' Share, CLASS 'BSS' Share OPTION Modname='GO-HELLO_DLL-Vl(O)' OPTION Map=hello.mpd OPTION Quiet,NOD,Verbose For contrast, here is the command file for the Hello World (custom window) executable file: SYSTEM PenPoint NAME \386\PENPOINT\app\hello\hello.exe DEBUG ALL FILE heUbl.obj FILE hello. obj LIBRARY penpoint LIBRARY app OPTION Quiet, Map=hello.mpe, NOD, Verbose, Stack=15000, MODNAME='GO-HELLO_EXE-Vl(O)' In addition to this command file, the WATCOM linker also requires a DLL.LBC file. This file lists all the exported functions defined in the DLL being linked. Usually, PenPoint DLLs only have the single entry point DLLMain. The lines in the DLL.LBC file have the form: ++entry-point.go-lname The case doesn't matter in the DLL.LBC file. The go-lname is used by the PenPoint installer to identifY code modules. An Iname is composed of a company ID, a project name, and a revision number in the forin Vmajor (minor) . Where major is the major revision number and minor is the minor revision number. Thus, for Hello World (custom window), the DLL.LBC file contains the single line: ++DLLMAIN.'GO-HELLO_DLL-Vl(O)' CHAPTER 8 I CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) Hello World (Custom Window) ~,. DLe Fiies 8.1.1.2 Because Hello World (custom window) requires that HELLO.OLL be loaded before HELLO.EXE can run, you need to have a HELLO.DLC file in the Hello World , (custom window) application directory that expresses the relationship: GO-HELLO_DLL-Vl(O) GO-HELLO_EXE-Vl(O) hello.dll hello.exe The PenPoint installer uses this information when installing the Hello World (custom window) application. The first line indicates that the Hello World (custom window) application depends on the DLL file HELLO.OLL, version 1(0). Should this DLL already be loaded, PenPoint will not attempt to load it. The second line tells PenPoint to install the executable file HELLO.EXE. Because the PenPoint name of the application directory is "Hello World," the makefile must STAMP the .OLC file with the name "Hello World" so that the Installer will find it. ~ Interesting Things You Can Do with Hello 8.1.2 Notice how the font scales in Hello World and how it uses shades of gray. ~ Highlights of clsHelloW~rld 8.1.3 The method table for clsHelloWorld handles two significant messages: msgAppOpen, msgAppClose, "HelloOpen", IHelloClose", objCallAneestorAfter, objCallAneestorBefore The handler for msgAppOpen creates an instance ofclsHelloWin and inserts it as the frame's client window. The handler for msgAppClose destroys the client window. When processCount is 0, main calls ClsHelloInit. ~ Highlights of clsHelioWin 8.1.4 TheDLLMain for clsHelloWin is the only thing defined in OLLINIT.C. The DLLMain calls ClsHelloWinInit, the initialization routine for clsHelloWin. STATUS EXPORTED DLLMain (void) StsRet(ClsHelloWinInit(), s); The method table for clsHelloWin (in HELWTBL.TBL) handles three significant messages: msgInit, msgFree, msgWinRepaint IHelloWinInit", IHelloWinFree", . IHelloWinRepaint", objCallAneestorBefore, objCallAneestorAfter, o clsHelloWin is the first sample application that defines its own instance data (in HELLOWIN.C). typedef struet INSTANCE DATA SYSDC de; INSTANCE_DATA; 127 128 PEN POINT APPLICATION WRITING GUIDE clsHelloWin responds to msgInit by zeroing the instance data, creating a drawing context, initializing the drawing context, and storing the drawing context in the hello window object's instance data. The class responds to msgDestroy by destroying the drawing context. clsHelloWin responds to msgWinRepaint by calculating the text width and scaling the window so that it fits the text ~ Graphics Overview 8.2 To draw in a window you need to· create a drawing context object (often abbreviated DC). You send messages to the drawing context, not your window, to draw. The drawing context's class knows how to perform these graphics operations. There could be different kinds of drawing contexts to choose from on PenPoint; for example, there might be one available from a third-party company which understands 3-D graphics, or you could create your own. ".. System Drawing Context 8.2.1 The standard system drawing context supports the ImagePoint imaging model. You can draw lines, polygons, ellipses, Bezier curves, and text by sending messages to an instance of the system drawing context. Each of these graphic operations is affected by the current graphics state of your DC. The system drawing context strokes lines and the borders of figures with the current line pattern, width, end style, and corner style, all of which you can set and get using SysDC messages. Similarly, it fills figures with the current fill pattern. Most drawing operations involve both stroking and filling a figure, but by adjusting line width and setting patterns to transparent, you can only fill or only stroke a figure. The pixels of figures on the screen are transformed according to the current rasterOp. This is a mathematical description of how the destination pixels on the screen are affected by the pixels in the source figure. To paint over pixels on the screen, you use the default rasterOp, sysDcRopCopy; another common rasterOp is sysDcRopXOR, which inverts pixels on the screen. At this writing there are no PenPoint computers that support color, however, the system drawing context supports afull color model. You can set the background and foreground colors (on a black and white display, the resulting colors on a will always be black, white, or a shade of gray). The line and fill patterns are mixtures of the current foreground and background color, or sysDclnkTransparent. Because the system drawing context is a normal Class Manager object, you create a new instance of it in the usual way, by sending msgNew to clsSysDrwCtx. Your drawing messages end up on some window on the screen,so at some point you must bind your DC to the desired window using msgDcSetWindow. If you want to draw temporarily on the screen, it's better to set the sysDcDrawDynamic mode instead of directly changing the rasterOp. CHAPTER 8 / CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) When to Create Things? ?-' Coordinai'es in Drawing (oni'exi 8.2.2 Another vital property of the system drawing context is its arbitrary coordinate system. You can choose whether one unit in your drawing {as in "draw a line one unit long" is one point, 0.01 mm, 0.001 inch, 1120 of a point, one pixel on the final device. You can then scale units in both the X and Y direction; one useful scaling is to scale them relative to the height and width of your window. You can even rotate your coordinate system. What this gives you is the precision of knowing that your drawing will be an exact size. It also gives you the freedom to use any coordinate system and scale that suits your drawing. The default coordinates are one unit is one point (approximately 1172 of an inch), and the origin is in the lower left corner of your window. Hello World (custom window) uses the default units, but scales its coordinate system so that its text output remains at a regular aspect ratio. "., When to Paint 8.2.3 Windows need to repaint themselves when they first appear on the screen, when they are is resized, and when they are exposed after other windows have covered them. Windows receive msgWinRepaint when the window system determines that they need to repaint, and windows must respond to this. clsHelloWin only paints in response to msgWinRepaint. The way most windows work is that they repaint dirty areas rather than paint newones. When awindow wants to draw something new, it can dirty itself and hence will receive msgWinRepaint. clsHelloWin has no need to dirty itself since it doesn't change . what it paints. ".When 10 Creale Things? The need to manage a separate object (a drawing context) introduces two crucial questions you need to consider when designing an application: • When do I create and destroy an object (or resource)? • When do I file it, if at all? An application can create objects at many stages in its life. It can create objects at installation, at initialization (or at restore time), when opening, or when painting its windows. If your application can create an object just before it needs it, the less memory it consumes in earlier stages. But it takes time to create objects, so you must trade off memory savings with speed. To decide when to create objects, you need to work backwards from when they are needed. In this case, Hello World only needs a drawing context in its window's repaint routine. Creating a DC every time you need to repa~nt is OK, but it is a fairly expensive operation. Besides, realistic applications often use a DC in input processing as well, to figure out where the user's pen is in convenient coordinates. However, we do know that a DC will never be needed when the view doesn't exist. 8.3 129 130 PENPOINT APPLICATION WRITING GUIDE clsHello could create the DC and pass it to clsHelloWin, but it's usually much more straightforward for the objectthat needs another object to create that object. Hello World creates its window when it receives msgAppOpen and destroys its window when it receives msgAppClose. These ~re reasonable times for the window to create its DC, so clsHelloWin creates a DC when it receives msglnit and destroys the DC when it receives msgFree. ".. Instance Data 8.3.1 In our example, clsHelloWin creates its DC in advance. This means that it has to store the UID of the DC somewhere so that it can use ir during msgWinRepaint. In typical DOS C programs, you can declare static variables to hold information. It is possible to do this in PenPoint, but in general you should not do it in object-oriented code. Instead, you should store the information inside each object, in its instance data. Up until now our classes have not had to remember state, so they haven't needed their own instance data. (Even if the class you create does not define instance data for its objects, its ancestors define some instance data, such as the document name and the label of the toolkit field.) Specifying instance data is easy. You just tell the Class Manager how big it is (in the class. size field) when you create your class. You would typically define a typedef for the structure of your class's instance data, then give the size of this as the class.size. In the case of dsHelloWin, we define·a structure called " INSTANCE_DATA: typedef struct INSTANCE_DATA { SYSDC dc; } INSTANCE_DATA, *P_INSTANCE_DATA; and then in CIsHelloWinInit: STATUS ClsHelloWinlnit (void) ( CLASS NEW new; STATUS s; II Create the class. ObjCallWarn(msgNewDefaults, clsClass, &new); new.object.uid = clsHelloWin; new.cls.pMsg = clsHelloWinTablei new.cls.ancestor = clsWin; new.cIs.size = SizeOf(INSTANCE_DATA); new.cls.newArgsSize = SizeOf(HELLO_WIN_NEW); ObjCallRet(msgNew, clsClass, &new, S)i ".. Is It msgNew or msglnit? .& we discussed, dsHelloWin creates its DC when it is created. It does this by responding to msgInit. Note that dsHelloWin responds to msglnit, not msgNew. When you create an object, you send its class msgNew. No classes intercept this message, so it goes up 8.3.2 CHAPTER 8 I CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) When to Create Things? the ancestor chain to clsClass, which creates the new object. The Class Manager then sends msgInit to the newly-created object, so that it can initialize itself. ". Window Initialization 8.3.3 Here's the HelloWinInit code which creates the Hello Window in response to msgInit: MsgHand~er(HelloWinlnit) { SYSDC NEW INSTANCE DATA SYSDC FONT SPEC SCALE STATUS dn; data; fs; fontScalei Si clsHelloWinInit declares an instance data structure. It does this because the pointer to instance data passed to message handlers by the Class Manager (pData, unused in this routine) is read-only. It then initializes the instance data to zero. It's important for instance data to be in a well-known state. This isn't necessary in the case of clsHelloWin, since the <:>nly instance data is the DC UID that it will fill in, but it is good programming practice. II Null the instance data. memset(&data, 0, sizeof(INSTANCE_DATA»; clsHelloWin then creates a DC: II Create a dc. ObjCallWarn(msgNewDefaults, clsSysDrwCtx, &dn); ObjCallRet(msgNew,clsSysDrwCtx, &dn, s); When msgNew returns, it passes back the UID of the new system drawing context. This is what clsHelloWin wants for its instance data: data.dc = dn.object.uid; clsHelloWin sets the desired DC state (including the line thickness) and binds it to self (the instance that has just been created when HelloWinInit is called): II Rounded lines, thickness of zero. ObjectCall(msgDcSetLineThickness, data.dc, (P_ARGS)O); i f (DbgFlagGet (' F', Ox40L» ( Debugf("Use a non-zero line thickness."); ObjectCall(msgDcSetLineThickness, data. dc, (P_ARGS)2); II II Open a font. Use the "user input" font (whatever the user has chosen for this in System Preferences. fs.id 0; fs.attr.group sysDcGroupUserlnput; fs.attr.weight sysDcWeightNormal; fs.attr.aspect sysDcAspectNormal; fs.attr.italic 0; fs.attr.monospaced 0; fs.attr.encoding sysDcEncodeGoSystem; ObjCallJmp(msgDcOpenFont, data. dc, &fs, s, Error); 131 132 PENPOINT APPLICATION WRITING GUIDE II II II Scale the font. The entire DC will be scaled in the repaint to pleasingly fill the window. fontSeale.x = fontSeale.y = FxMakeFixed(initFontSeale,O); ObjeetCall(msgDeSealeFont, data.de, &fontSeale); II Bind the window to the de. ObjeetCall(msgDeSetWindow, data. de, (P_ARGS)self); At this point, clsHelloWin has set up its instance data in a local structure. It calls ObjectWrite to get the Class Manager to update the instance data stored in the Hello Window instance: II Update the instance data. ObjeetWrite(self, etx,&data); return stsOK; ".. Using Instance Data 8.4 Accessing instance data is easy. The Class Manager passes a read-only pointer to instance data into the class's message handlers. The Class Manager has no idea what the instance data is, so it just declares the pointer as a mystery type (p_DATA, which is defined as P_UNKNOWN). The MsgHandler macro names the pointer pData. c1sHelloWin needs to access its instance data during msgWinRepaint handling so it can use the DC. It knows that the instance data pointed to by pData is type INSTANCE_DATA, so it uses the MsgHandlerWithTypes macro, which allows it to provide the.types (or casts) for the argument and instance data pointers: MsgHandlerWithTypes(HelloWinRepaint, P_ARGS, P_INSTANCE_DATA) You can pass the pData pointer around freely within your code, but whenever you want to change instance data, you must dereference it into a local (writable) variable, modify the local variable, and then call ObjectWrite. c1sHelloWin creates its DC when it is created, and never changes it, so it doesn't have to worry about de-referencing its instance data into local storage. But c1sCntr, described in Chapter 9, does have to do this . .", No Filing Yet On a page turn, the process and all objects associated with a Hello World (custom window) document are destroyed. Normally this means that objects have to file their state. However, since clsHelloWin destroys its DC when it is destroyed and never changes its DC's state, it doesn't have to file its DC. The application does not file its view-it creates it at msgAppOpen to draw, then destroys it at msgAppClose, and there's no useful state to remember from the DC. You could imagine an application which would want to remember some of the state of its DC. For example, if the user could choose the font in Hello World (custom window), then the program would need to remember what the font was 8.4.1 CHAPTER 8 / CREATING A NEW CLASS (HELLO WORLD: CUSTOM WINDOW) Debugging Hello World (Custom Window) so that when the user turns back to the application's page the application continues to use the same font. Drawing in a Window 8.5 Empty Application prints out messages, but it doesn't draw them in its window. Instead it uses the error output routine Debugf to generate output. Hello World (custom window) actually draws something in its window. Windows are separate objects from applications, and the window gets told to repaint, not the application. Hence you need to create a window object. The window object will receive msgWinRepaint messages whenever it needs to paint its window, either because the application has just appeared on-screen, or because another window was obscuring part of this window. clsWin responds to msgWinRepaint by filling self with the background color and outlining the edge of the window. You could put an instance of clsWin inside your frame, but we want something more interesting to appear in the window. So clsHelloWin intercepts msgWinRepaint and draws its own thing. It draws the strings "Hello" and "World" and then draws an exclamation point using graphics commands. The most complex thing about its repaint routine is its scaling. It measures how long the strings "Hello" and "World" will be, then uses this information to scale its coordinate system so that the words and drawing fit in the window nicely. Possible Enhancements 8.6 Try drawing some other shapes using other msgDcDraw... messages. Nest a clsHelloWin window in the custom layout window from HELLOTK2.C. Debugging Mello World (Custom Window) If you want to modify Hello World (custom window), you might need to use DB extensively as you make changes. This section explains techniques developers . commonly use to speed up debugging with DB. To save typing commands over and over to DB, you can store them in files and read them into DB using its < command, for example: < \\boot\proj\setbreak.txt When it starts, DB looks for a start-up file called DBCUSTOM.DB. It tries to find this in \\BOOT\PENPOINT\APP\DB, but you can specify the path to another file by specifying the path in a DBCustom line in \PENPOINT\BOOT\ENVIRON.lNI, You can use DBCUSTOM.DB to set up the ctx and s:rcdi:r for your application's executables and DLLs, and set breakpoints. Here's how a DBCUSTOM.DB for Hello World (custom window) might look: 8.7 133 134 PENPOINT APPLICATION WRITING GUIDE sym "go-hello exe-vl(O)" \\boot\penpoint\sdk\sample\hello\hello.exe srcdir "go-hello exe-vl(O) " \\boot\penpoint\sdk\sample\hello sym "go-hello dll-vl(O) " \\boot\penpoint\sdk\sample\hello\hello.dll srcdir "go-hello dll-vl(O) " \\boot\penpoint\sdk\sample\hello bp HelloWinRepaint g Whenever you start a new instance of Hello World (custom window) (either by choosing from the Accessory palette or by turning a page), DB will halt. At that point you can type t to step a line, q to continue, and so on. Chapter 9 / Saving· and Restoring Data (Counter App) The sample programs we have considered so far do not have any information to save. They always do the same thing in response to the same messages. However, real applications must be able to save and restore data. PenPoint applications also have information about what is on screen, where was the user interacting with the application last, what options were set, what controls were active at the time, and so on. This information together with the application's data is called the application's state. Because PenPoint is an object oriented system, there is no real distinction between data and state information. An application is built from a series of objects. A scribble object might contain the scribbles that the user just drew, while a scroll window object contains the current scrolling position of the window. The former contains "user data" and the latter contains state information, but to PenPoint they are simply objects. This chapter discusses how applications save and restore their data. The last part of this chapter describes how to create a menu using clsTkTable. Saving State 9.1 Remember that as the user turns from page to page in the Notebook, the Application Framework is starting up and shutting down instances of clsApp. When you turn the page from Empty Application or Hello World, the Application Framework destroys the clsEmptyApp or clsHelloWorld application object. When you turn back to that page, the Application Framework creates a new application object. Note The baSic rule for filing This is fine, because these applications don't need to remember anything. They state is: If I don't file this state, start from scratch each time they appear. However, if applications do change state, will users notice that the they must preserve this state, so that the user is not aware that an application application is different when they turn back to its page? instance "behind" what is seen onscreen is coming and going. ~ Counter Application The Counter Application saves data. Each time the application appears on-screen, it increments a counter and displays the counter's value. It also lets the user choose the format in which to display the counter (decimal, octal, or hexadecimal). 9.2 136 PENPOINT APPLICATION WRITING GUIDE Based on the state filing rule, the application has two pieces of state that it should file: • The value of the counter • The format in which it was told to display the counter. dsCntrApp remembers the format in which.it displays the counter value. dsCntrApp could also remember the value of the counter, but one of the benefits . of an object-oriented system is that you can break up your application code into objects which model the natural structure of the system. It's natural to see the application as displaying the value of a separate object, so that's the way we implement it: dsCntrApp creates and interacts with a separate dsCntr object. Because the format could be applied to all counter objects in the application, dsCntrApp also remembers the format. Note the difference between CounterApp and the two Hello Wor~d sample programs. The Hello World applications hadto create other objects to get the behavior they needed. An application object is not a window, so they had to create window objects. In the case ofCounterApp's counter object, we're not forced to use a separate object-we could have dsCntrApp remember the state of the counter, but for design reasons we choose to implement the counter as a separate object. PenPoint has several classes that store a numeric value: clsIntegerField A handwriting field that accepts numeric input clsPageNum The page number in floating frames clsCounter The page number with up and down arrows in the Notebook These are all window classes that display the· numeric value. clsCntrApp creates a label to display the value of the counter, much like Hello World (toolkit). Hence none of these are quite right for CounterApp, so we create a separate counter class. This figure shows the classes defined by CounterApp and their ancestors. Because the UI Toolkit uses the symbol clsCounter already, CounterApp uses the symbol clsCntr for its coun·ter class. CHAPTER 9 I SAVING AND RESTORING DATA (COUNTER APP) Counter Application figure 9-1 CounterApp Obiects PenPoint provides: You must write: ". Compiling and Installing the Application 9.2.1 To compile CounterApp, change to the \PENPOINT\SDK\SAMPLE\CNTRAPP directory and type wmake. This creates a \PENPOINT\APP\CNTRAPP directory and compiles CNTRAPP.EXE in that directory. Install CounterApp either by adding \\BOOT\PENPOINT\APP\Counter Application to \PENPOINT\BOOT\APP.INI before starting PenPoint or by installing the application using the Installer. ". Counter Application Highlights 9.2.2 The method table for clsCntrApp handles a number of interesting messages: msglnit, msgSave, msgRestore, msgFree, msgAppInit, msgAppOpen, msgAppClose, msgCntrAppChangeFormat, "CntrApplnit", "CntrAppSave", "CntrAppRestore" , "CntrAppFree", "CntrAppApplnit" , "CIitrAppOpen" , "CntrAppClose" , "CntrAppChangeFormat" , objCallAnCestprBefore, objCallAncestorBefore, objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, 0, 137 138 PENPOINT APPLICATION WRITING GUIDE clsCntrApp creates an instance of clsCntr at msgAppInit time. I method table clsCntrApp responds to msgAppOpen by incrementing the counter, creating a label containing the counter value, making the label the client window, and creating the menu bar. #defines, Iypedefs message handlers doss initialization clsCntrApp responds to msgAppClose by destroying the client window. main entry point The class responds to msgCntrAppChangeFormat, which is sent by its menu buttons, by changing its stored data format. When processCount is 0, main calls CIsCntrAppInit. ",. Counter Class Highlights 9.2.3 The method table for clsCntr is also defined in METHODS.TBL and handles these messages: msqNewDefaults, msqlnit, msqSave, msqRestore, msqFree, msqCntrGetValue, msgCntrlncr, "CntrNewDefaults" , "Cntrlnit" , "CntrSave" , "CntrRestore" , "CntrFree" , "CntrGetValue" , "Cntrlncr" , I method table objCallAncestorBefore, objCallAncestorBefore, objCallAncestorBefore, objCallAncestorBefore, objCallAncestorAfter, 0, 0, ",. Instance Data #defines, Iypedefs message handlers doss initialization main entry point 9.2.4 The instance data for a clsCntr object contains the value of the counter: typedef struct CNTR INST S32 currentValue; CNTR_INST, *P_CNTR_INST; Make sure you notice the difference between CNTR..JNST, the counter's instance data, and CNTR_INFO, .the structure used for the arguments passed with msgCntrGetValue. In this example, die two structures contain the same data; in a more !=omplex example, the instance data would contain all the stateful information required by an instance of the object, while the message argument structure would only contain the data needed by a particular message. Because the purpose of clsCntr is to maintain a value for its client, clsCntr must provide a means for its client to access the value. One common approach lets the client perform these tasks: • Specify an initial value in msgNew • Get the value with a special message • Set the value with a special message. clsCntr does all of these except set the value. The _NEW_ONLY information for clsCntr contains an initial value. Here is the CNTR_NEW_ONLY structure from CNTRH, Important pOint. I CHAPTER 9 I SAVING AND RESTORING DATA (COUNTER APP) Counter Application typedef struct CNTR_NEW_ONLY { S32 initialValue; } CNTR_NEW_ONLY, *P_CNTR_NEW_ONLY; In case its client doesn't specify an initial value when the client sends msgNew, clsCntr initializes the msgNewargument to a reasonable value (zero) in msgNewDefaults: MsgHandlerArgType(CntrNewDefaults, P_CNTR_NEW) { Debugf("Cntr:CntrNewDefaults"); II Set default value in new struct. pArgs->cntr.initialValue = 0; return stsOK; MsgHandlerParametersNoWarningi 1* CntrNewDefaults *1 In response to msgInit, clsCounter initializes the instance data to the starting value specified in the msgNew arguments: MsgHandlerArgType(CntrInit, P_CNTR_NEW) { CNTR_INST inst; Debugf ("Cntr: CntrInit ") ; II Set starting value. inst.currentValue = pArgs->cntr.initialValue; II Update instance data. ObjectWrite(self, ctx, &inst)i return stsOK; MsgHandlerParametersNoWarning; 1* CntrInit *1 ".. GeHing and SeHing Values 9.2.5 clsCntr defines messages to get and set the counter value, msgCntrGetValue and msgCntrInc. Note how we intentionally limit the API to suit the design of the object: the client can't directly set the counter value, it can only increment it. This makes the counter less general. The (dubious) advantage of the approach used is that if the design of clsCntr changes so that it has more information, CNTR_INFO could change to include more information, and clients of it would only need to recompile. ".,.. GeHing the Value The handler for msgCntrGetValue is straightforward. Note that the client must pass it a pointer to the structure in which clsCntr passes back the value. MsgHandlerWithTypes(CntrGetValue, P_CNTR_INFO, P_CNTR_INST) ( Debugf(ICntr:CntrGetValue"); pArgs->value = pData->currentValue; return stsOK; MsgHandlerParametersNoWarning; 1* CntrGetValue *1 9.2.5.1 139 140 PENPOINT APPLICATION WRITING GUIDE In this case, passing a CNTR_INFO structure as the message arguments is not necessary. msgCntrGetValue could take a pointer to an 532, instead of a pointer to a structure that contains an 532. However, as soon as you need more than 32 bits to communicate the message arguments, you must define a structure and pass a pointer to the structure. Incrementing the Value 9.2.5.2 msgCntrIncr increments the value. It doesn't take any arguments. MsgHandler(Cntrlncr) ( CNTR_INST inst; Debugf("Cntr:Cntrlncr"); inst = IDataDeref(pData, CNTR_INST); inst.currentValue++; ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; /* Cntrlncr */ Note A frequent source of There are a couple of things to note here. The instance data is stored in memory programming errors is trying to that only the Class Manager can write. When the Class Manager calls the message modifY read-only instance data. handler, it passes a pointer to this protected instance data (pData). If the code had tried to update pData->currentValue directly, PenPoint would have generated a general protection fault immediately. So, the code creates a local structure for the instance data and uses the IDataDeref macro to dereference the instance data pointer into local, writable, memory (inst). There is nothing mysterious about the IDataDeref macro. All it does is provide a short hand way of casting the instance data indicated by pData. IDataDeref is defined in CLSMGR.H as: #define IDataDeref(pData, type) (*(type*)pData) We could just as easily have written: inst = (CNTR_INST) pData; Mter incrementing the value in local memory, clsCntr calls ObjectWrite, which directs the Class Manager to update the instance data stored in the object. ObjectWrite(self, ctx, &inst); JY Obiect Filing The way objects preserve state is by filing it at the appropriate time. The Application Framework sends the application instance msg5ave when the document should save its state and msgRestore when the document should recreate itself. The order in which applications receive these and other messages from the Application Framework is explained in Part 2: Application Framework of the PenPoint Architectural Reference manual. Note Another freq uent source of programming errors is failing to update instance data with ObjectWrite after changing instance data. 9,3 Objects can also preserve state by refusing to be terminated, although this usually consumes memory. CHAP-TER 9 I SAVING AND RESTORING DATA (COUNTER APP) Object Filing The message arguments to msgSave and msgRestore include a handle on a resource file. Objects respond by writing out their state to this file and reading it back in. The objects do not care where the resource file is, nor do they care who created it. The Application Framework creates and manages a resource file for each document. The file handle passed by msgSave and msgRestore is for this resource file. If you start up the disk viewer with the B debug flag set to 800 hexadecimal, and expand \\RAM\PENPOINT\SYS\Notebook\CONTENTS\NotebookApplications, you should be able to see these files; look for a file called DOC.RES in each document directory. At the level of msgSave and msgRestore, classes can just write bytes to a file (the resource file) to save state. When it receives msgSave,· clsCntrApp could get the value of the counter object (by sending it msgCntrGetValue) and just write the number to the file. However, this would introduce dependencies between the two objects, which in object-oriented programming is a bad thing. So, instead clsCntrApp tells the counter object to file itself. We'll cover exactly how this happens later, but for now just accept that the clsCntr instance receives msgSave. ".. Handling msgSave The message argument to msgSaveis a pointer to an OBLSAVE structure: MsgHandlerArgType(CntrSave, P_OBJ_SAVE) If you look in \PENPOINT\SDK\INC\CLSMGR.H, you will notice that one of the fields in the OBLSAVE structure is the handle of the file to save to. So all clsCntr has to do is write that part of its instance data that it needs to save to the file: basically, all of its instance data. To write to a file, you send msgStreamWrite to the file handle. The message takes a pointer to a STREAM_READ_WRITE structure, in which you specify what to file arid how many bytes to write. MsgHandlerArgType(CntrSave, P_OBJ_SAVE) { STREAM_READ_WRITE fsWrite; STATUS s; Debugf("Cntr:CntrSave"); II II Write instance to the file. II fsWrite.numBytes= SizeOf(CNTR_INST); fsWrite.pBuf= pData; ObjCallRet(msgStreamWrite, pArgs->file, &fsWrite, s); return stsOK; MsgHandlerParametersNoWarning; 1* CntrSave *1 9.3.1 141 142 PENPOINT APPLICATION WRITING GUIDE msgStreamWrite passes back information about how many bytes it actually wrote. A real application would check this information to make sure that it successfully filed all its state. ".. Handling msgRestore 9,3,2 msgRestore is similar to msgSave. The Class Manager handles msgRestore by creating a new object, so the ancestor must be called first. The message argument to msgRestore is a pointer to an OBLRESTORE structure: MsgHandlerArgType(CntrRestore, P_OBJ_RESTORE) Again, one of the fields in this structure is the UID of the file handle to restore from. clsCntr just has to restore seWs instance data from the filed data. This is similar to initializing instance data in msgInit handling, except that the information has to be read from a file instead of from msgNew arguments. You declare a local instance data structure: MsgHandlerArgType(CntrRestore, P_OBJ_RESTORE) { CNTR_INST inst; STREAM_READ_WRITE fsRead; STATUS s; To read from a file, you send msgStreamRead to the file handle, which takes a pointer to the same STREAM_READ_WRITE structure as msgStreamWrite. In the structure you specify how many bytes to read and give a pointer to your buffer that will receive the data: Debugf ("Cntr:CntrRestore") ; II II Read instance data from the file. II fsRead.numBytes= SizeOf(CNTR_INST); fsRead.pBuf= &inst; ObjCallRet(msgStreamRead, pArgs->file, &fsRead, s); You call ObjectWrite to update the object's instance data. II II II Update instance data. ObjectWrite (self,. ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; 1* CntrRestore *1 ".. CounterApp's Instance Data clsCntrApp's instance data contains: • The display format to use for the counter value • The UID of the counter object • A memory-mapped file handle (explained below). 9.4 CHAPTER 9 I SAVING AND RESTORING DATA (COUNTER APP) 143 CounterApp's Instance Dato When the user turns away from a CounterApp document's page, the Application Framework destroys the counter object (and the application instance). When the user turns back to the CounterApp document, the counter object is restored with a different UID. Hence clsCntrApp should not file the UID of the counter object, because it will be invalid upon restore. clsCntrApp only needs to file the display format and to tell the counter object to save its data. ",. Memory-Mapped File CounterApp could just write its data to the resource file created by the Application Framework, just as the counter object did. However, a disadvantage of filing d~ta is that there are two copies of the information when a document is open: the instance data in the object maintained by the Class Manager and the filed data in the document resource file maintained by the file system. Tip. Saving the UIDs of an object is usually incorrect. Either the object has a well-known UID (there's no reason to file it), or the UID is dynamic (it will be different when restored). 9.4.1 CounterApp and the counter object use different filing methods. This is useful when you need to differentiate between instance data and other forms of data. One way to avoid this duplication of data is to use a memory-mapped file. Instead of reading and writing to a file, you can simply map the file into your address space; reading and writing to the file take place transparently as you access that memory. clsCntrApp stores its data (the current representation) in a memory-mapped file. "'''' Opening and Closing The File 9.4.2 Because you need to open the file both when creating the document for the first time, and when restoring the document after it has been filed, you need to open the file in two different places (msgAppInit and msgRestore), but you only need close it in one place (msgFree). Why close the file in response to msgFree? Why not msgSave? Remember that when an application is created, it is sent msgApplnit (in response to which it creates and initializes objects) and then is immediately sent msgSave (which allows it to save its newly initialized objects before doing anything else). msgSave is also sent when the user checkpoints a document. In other words, receiving msgSave doesn't necessarily mean that we're about to destroy the application object. ,.,.,.. Opening For the First Time When CounterApp receives msgApplnit, it creates the counter object: MsgHandler(CntrAppAppInit) { CNTR NEW en; FS- NEW fsn; STREAM READ WRITE fsWrite; CNTRAPP_DISPLAY_FORMAT ·format; CNTRAPP INST inst; STATUS Debugf ("CntrApp: CntrAppAppInit "); inst = IDataDeref(pData, CNTRAPP_INST); 9.4.2.1 144 PENPOINT APPLICATION WRITING GUIDE II II II Create the counter object. ObjCallRet(msgNewDefaults, clsCntr, &Cn, s); cn.cntr.initialValue = 42; ObjCallRet(msgNew, clsCntr, &cn, s); inst.counter = cn.object.uid; CntrAppAppInit opens the file (called FORMATFILE), initializes the format value, and writes the initial data to the file: II II II Create a file, fill it with a default value ObjCallRet(msgNewDefaults, clsFileHandle, &fsn, s); fsn.fs.locator.pPath = "formatfile"; fsn.fs.locator.uid = theWorkingDir; ObjCallRet(msgNew, clsFileHandle, &fsn, s); format = dec; fsWrite.numBytes = SizeOf(CNTRAPP_DISPLAY_FORMAT); fsWrite.pBuf = &format; ObjCallRet(msgStreamWrite, fsn.object.uid, &fsWrite, s); Mapping the file to memory is quite straightfoward. CounterApp sends msgFSMemoryMap to the file handle, and passes the pointer to the address pointer (pp _MEM). Finally updates its instance data and returns: II II Map the file to memory ObjCallRet(msgFSMemoryMap, fsn.object.uid, &inst.pFormat, s); II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; 1* CntrAppAppInit *1 Opening To Restore CntrAppRestore is essentially similar to CntrAppApplnit, only after creating a handle on FORMATFILE, CntrAppRestore maps the file to memory, which immediately makes the format data available in inst.pFormat. MsgHandlerWithTypes(CntrAppRestore, P_OBJ_RESTORE, P_CNTRAPP_INST) ( FS NEW fsn; CNTRAPP INST inst; STATUS s; Debugf("CntrApp:CntrAppRestore"); II Get handle for format file, save the handle ObjCallRet(msgNewDefaults, clsFileHandle, &fsn, s); fsn.fs.locator.pPath = "formatfile"; fsn.fs.locator.uid = theWorkingDir; ObjCallRet(msgNew, clsFileHandle, &fsn, s); inst.fileHandle = fsn.object.uid; II Map the file to memory ObjCallRet(msgFSMemoryMap, fsn.object~uid, &inst.pFormat, s ); CHAPTER 9 / SAVING AND RESTORING DATA (COUNTER APP) CounterApp's Instance Data (~osing On msglFree When the application receives msgFree, it destroys the counter object, sends msgFSMemoryMapFree to unmap the file, and then sends msgDestroy to the file handle to close the file. MsgHandlerWithTypes(CntrAppFree, P_ARGS, P_CNTRAPP_INST) ( STATUS s; Debugf(ICntrApp:CntrAppFree"); ObjCallRet(rnsgDestroy, pData->counter, Nil(P_ARGS), s); II Unrnap the file ObjCallRet(rnsgFSMernoryMapFree, pData->fileHandle, Nil(P_ARGS), s); II Free the file handle .ObjCallRet(rnsgDestroy, pData->fileHandle, Nil(P_ARGS), s ); return stsOK; MsgHandlerPararnetersNoWarning; 1* CntrAppFree *1 Filing the Counter Obied The only thing that is left to do is to tell the counter object when to save and restore its data. For this, you send the resource messages msgResPutObject and msgResGetObject to the resource file handle created by the Application Framework. These messages are defined in RESFILE.H. You do not send msgSave and msgRestore directly to the counter object. The resource file handle is an instance of clsResFile. When you send a message to the resource file handle, you tell it which object you want to put or get. In the case of msgResPutObject, clsResFile writes information about the object to the resource file, then sends msgSave to the object. In the case of msgResGetObject, clsResFile reads information about the object from the file, creates the object, which is essentially empty until clsResFile sends msgRestore to the object. This is how objects receive msgSave and msgRestore. Saving the Counter Object When CounterApp receives msgSave, it sends msgResPutObject to the file handle passed in with the msgSave arguments. MsgHandlerWithTypes(CntrAppSave, P OBJ_SAVE, P_CNTRAPP_INST) { STATUS s; Debugf(ICntrApp:CntrAppSave"); II Save the counter object. ObjCallRet(rnsgResPutObject, pArgs->file, pData->counter, s); return stsOK; MsgHandlerPararnetersNoWarning; 1* CntrAppSave *1 CounterApp doesn't have to save its own data, because the file FORMATFILE is memory mapped to its data. 145 146 PEN POINT APPLICATION WRITING GUIDE "..,.. Restoring the Counter Obiect 9.4.3.2 When CounterApp receives msgRestore, it sends msgResPutObject to the file handle passed in with the msgRestore arguments. MsgHandlerWithTypes(CntrAppRestore, P_OBJ_RESTORE, P_CNTRAPP_INST). { FS NEW fsn; CNTRAPP_INST inst; STATUS s; (Open the memory mapped file.) II Restore the counter object. ObjCallJmp(ms'gResGetObject, pArgs->file, &inst.counter, s, Error); II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; Error: return s; 1* CntrAppRestore *1 ". Menu Support 9.S clsCntrApp creates a menu by specifying the contents of the menu statically in a toolkit table. clsTkTable is the ancestor of several VI components which display a set of windows, including choices, option tables, and menus. I,nstead of creating each of the items in a toolkit table by sending msgNew over and over to different classes, you can specify in.a set of toolkit table entries what should be in the table. When you send msgNew to clsTkTable (or one of its descendants) it creates its child items based on the information you gave it. When it receives msgAppOpen, clsCntrApp appends its menu to the SAMs (standard application menus) by passing its menu as an argument to msgAppCreateMenuBar. MsgHandlerWithTypes(CntrAppOpen, P_ARGS, P_CNTRAPP_INST) ( APP_METRICS .MENU NEW LABEL NEW STATUS char ami ron; In; s; buf[30)i II Get app metrics. ObjCallJmp(msgAppGetMetrics, self, &am, s, Error); II Set the label as the clientWin. ObjCallJmp(msgFrameSetClientWin, am.mainWin, In.object.uid, s, Error); II Create and add menu bar. CHAPTER 9 / SAVING AND RESTORING DATA (COUNTER APP) Menu Support ObjCallJmp(msgNewDefaults, clsMenu, &mn, s, Error); mn.tkTable.client = self; mn.tkTable.pEntries = CntrAppMenuBar; ObjCallJmp(msgNew, clsMenu~ &mn, s, Error); ObjCallJmp(msgAppCreateMenuBar, self, &mn.object.uid, s, Error); ObjCallJmp(msgFrameSetMenuBar, am.mainWin, mn.object.uid, s, Error); Buttons 9.5.1 The items in the menu are a set of buttons. When you create a button in toolkit table entry, you specify: • The button's string • The notification message the button should send when the user activates it • A value for the button. There are other fields in a TK_TABLE_ENTRY, but you can rely on their defaults ofO. The menu bar used in CounterApp is described by the TK_TABLE_ENTRY structure named CntrAppMenuBar in CNTRAPP.C. typedef enum CNTRAPP_DISPLAY_FORMAT dec, oct, hex CNTRAPP_DISPLAY_FORMAT, *P_CNTRAPP_DISPLAY_FORMAT; typedef struct CNTRAPP_INST { P CNTRAPP DISPLAY FORMAT OBJECT OBJECT pFormat; fileHandle; counter; CNTRAPP_INST, *P_CNTRAPP_INST; static TK_TABLE_ENTRY ~ntrAppMenuBar[] = { {"Representation", 0, 0, 0, tkMenuPullDown, clsMenuButton}, {"Dec", msgCntrAppChangeFormat, dec}, {"Oct", msgCntrAppChangeFormat, oct}, {"Hex", msgCntrAppChangeFormat, hex}, {pNull} , {pNull} }; When the user taps one of the buttons, it sends msgCntrAppChangeFormat to its client, which by default is the application. The message argument is the value of the button (dec, oct, or hex). clsCntrApp's message handler for msgCntrAppChangeFormat looks at the message argument to determine which button the user tapped. MsgHandlerWithTypes(CntrAppChangeFormat, P_ARGS, P_CNTRAPP_INST) { APP METRICS WIN STATUS char am; thelabel; s; buf[30]; 147 . 148 PENPOINT APPLICATION WRITING GUIDE Debugf(ICntrApp:CntrAppChangeFormat"); II II Update mmap data II * (pData->pFormat) = (CNTRAPP_DISPLAY_FORMAT) (U32)pArgs; II Build the string for the label. StsRet(BuildString(buf, pData), s); II Get app metrics. ObjCallRet(msgAppGetMetrics, self, &am, s); II Get the clientWin. ObjCallRet(msgFrameGetClientWin, am.mainWin, &thelabel, s); II Set the label string. ObjCallRet(msgLabelSetString, thelabel, buf, s); return stsOK; MsgHandlerParametersNoWarning; 1* CntrAppChangeFormat *1 Chapter 10 / Handling Input (Tic-Tac-Toe) Tic-Tac-Toe is a large, robust application whjch demonstrates how to "play along" with many of the PenPoint protocols affecting applications: • SAMs (standard application menus) • Selections • Move!copy protocol • Keyboard input focus • Stationery • Help • Option sheets. The rest of this chapter details the architecture ofTic-Tac-Toe, its files, classes, objects, etc, and describes some enhanced application features implemented in Tic-T ac-Toe. ,,-'ic-'ac-'oe Obiects 10.1 No tutorial of this size can give you a course in object-oriented program design. It is an art, not a science. The books mentioned in Chapter 3 will be helpful. No matter what your experience level, you will find that you redesign your object hierarchy at least once. (Here at GO, we redesigned our class hierarchy countless times in the first two years-now they're quite stable.) But there are some generally-accepted techniques for breaking up an application into manageable components, and this tutorial will lead you through them. Each section from now on will discuss the various design choices made. ~ Application Components A typical functional application does something in its application window, then saves data in the document working directory. Tic-Tac-Toe does this: it displays a tic-tac-toe board in its window, then stores the state of the board. Its application class is clsTttApp. The application creates its own class to display the board, clsTttView. It also creates a separate object just to store the state of the board, clsTttData. 10.1.1 '50 PEN POINT APPLICATION WRITING GUIDE Figure 10-1 Tic-Tac-Toe Classes and Instances PenPoint provides: Objects in a running instance of your application ",. Separate Stateful Data Obiects The tic-tac-toe data object's set ofXs and Os are the main part of its state, which it must preserve. The application and view also maintain some state, the application files its version, and the view remembers how thick to ,draw the lines on thetic-tac-toe board. 10.1.2 CHAPTER 10 I HANDLING INPUT (TIC-TAC-TOE) 151 Tic-Tac-Tae Window F""'~~c'~~c'@~ ~~~Qg~~Qg~~ 10.2 This table lists the files in Tic-T ac-Toe: Table 10-1 Tic-Tac-Toe Files File Name Purpose METHODS.TBL Message tables for clsTttApp, clsTttView, and clsTttData. TTTPRIV.H TttDbgHelper support macro and debug flags, TTT3ERSION typedef, function definitions for routines in TTTUTIL.C and debugging routines in TTTDBG.C, and class UID definitions. TTTAPP.C Implements the main routine and most of clsTttApp's message handlers. TTTVIEW.C Implements most of clsTttView, handling repaint and input. TTTDATA.C Implements clsTttData. TTTUTIL.C Utility routines to create scrollwin, create and adjust menu sections, read and write filed data and version numbers, get application components, handle selection. Also application-specific routines to manipulate Tic-Tac-Toe square values. TTTVOPT.C Message handlers for the option sheet protocol. TTTVXFER,C Message handlers for the move/copy selection transfer protocol. TTDBG.C Miscellaneous routines supporting the Debug menu choices (dump, trace, force repaint, etc.). TTTMBAR.C Defines the TK_TABLE_ENTRYarrays for Tic-Tac-Toe's menu bar. TTTAPP.H Defines clsTttApp messages. TTTVIEW.H Defines clsTttView messages and their message argument structures, and defines tags used in the view's option sheet. TTTDATA.H Defines possible square values, various Tic-Tac-Toe data structures, and clsTttView messages and their message argument structures. . Sets up UID-to-string translation tables which the Class Manager uses to provide more informative debugging output. ".. Tic-Tac-Toe Window There is no pre-existing class which draws letters in a rectangular grid. So, some work is needed here. The PenPoint UI Toolkit provides labels which can have borders, along with clsTableLayout which lets you position windows in a regular grid. So, you could create the tic-tac-toe board by creating nine of one-character labels in a table layout window. However, there are some problems with this: • Labels don't (ordinarily) scale to fit the space available . • Each label is a window. Windows are relatively "cheap" in PenPoint, but if we were to change to a 16 x 16 board, it would use 256 single-character label windows. So, to start with, we'll create a single window and draw the squares in this. 10.3 152 PENPOINT APPLICATION WRITING GUIDE "... Coordinate System 10.3.1 The obvious coordinate system is one unit is one square. However, this system makes it difficult to position characters within a square, since you specify coordinates for drawing operations in S32 coordinates. The tic-tac-toe view uses local window coordinates for its drawing. "... Advanced Repainting Str~tegy 10.3.2 As explained in Hello World, the window system tells windows to repaint. When a window receives msgWinRepaint, it always sends self msgWinBeginRepaint. This sets up the update region of the window-the part of the window where pixels can be altered-to the part of the window that needs repainting. After sending msgWinBeginRepaint, a window can only affect its pixels which the window system thinks need repainting, no matterwhere the window tries to paint. Because the window system must calculate this dirty area, it makes the area available to advan~ed clients by passing it back in the message argument structure of msgWinBeginRepaint. In a fit of probable overkill, the Tic-Tac-Toe view is such an advanced client. Tic-T ac-T oe looks at the RECT32 structure passed back and figures out what parts of the grid lines and which squares it needs to repaint. It wants to do this in its own coordinate system, so it sends msgWinBeginRepaint to its DC. ". View anel Data Interaction 10.4 The Tic-Tac-Toe view displays what's in the data object, so it needs access to the data maintained by the data object. There are various ways that a view can get to this state. It could share memory pointers with the data object, or it could use the specialized function ObjectPeek to look directly at the data object's instance data.memory. However, both of these methods compromise the separation of view and data into two objects. A purer approach is to have the view object send the data object a message when it needs to know the data object's state, but you still have to decide whether the data object should pass the view its internal data structures or a well-defined public data structure. Theseare the classic problems of encapsulation and abstraction facedin object-oriented program design. ,..,.. Data Obiect Design 10.4.1 Tic-Tac-Toe's data object class, clsTttData, is similar to CounterApp's clsCntr. It lets its client perfo'rm these tasks: • Specify an initial board layout in msgNew • Get the value of all the squares (msgTttDataGetMetrics) • Set the value of all the squares (msgTttDataSetMetrics) • Set the value of a particular square (msgTttDataSetSquare). I I I CHAPTER 10 / HANDLING INPUT (TlC-TAC-TOE) The Selection and Keyboard Input clsTttData gets and sets the square values as part of getting and setting all of its metrics. The theory is that any client that wants to set and get this probably wants all the information about the data object. (In fact, clsTttData's instance metrics comprise only its square values.) Design decision. Instance Data vs. Instance Info 10.4.2 The instance data for clsTttApp, clsTttView, and clsTttData is a pointer in each case. The instance data points to a data structure outside the instance where the class stores the instance "information." There are some advantages to this: • You don't have to use ObjectWrite to update instance data every time state changes, since the pointer never changes • A class can have variable-sized instance information. It does mean that the class has to allocate space for the instance information. The Tic-Tac-Toe classes do this using OSHeapBlockAlloc in msgInit processing. Saving a Data Obiect 10.4.3 clsTttApp tells its view to file, and an instance of clsView automatically files its data object. Handling Failures During msglnit and msgRestore 10.4.4 msgInit and msgRestore both create objects. It is vital that the handlers for these messages guarantee that the object is initialized to some well-known state, even if your handler or some ancestor failed in some way, because after a failed creation, the object will in fact receive msgDestroy. Note how clsTttData writes appropriate data into its instance data even in the case of an error. MsgHandlerWithTypes(TttDataInit, P_TTT_DATA_NEW, PP_TTT_DATA_INST) { P_TTT_DATA_INST pInsti STATUS Si ({nn) ) // // Initialize for error recovery. /1 plnst Y The = pNull i Selection and Keyboard Input When a computer permits multiple windows on-screen, it must decide which window receives keyboard input. PenPoint uses a selection model, meaning that it sends keyboard input to the object holding the selection (along with all the other move/copy/options/delete messages which the selected object may receive). So, to allow typing, the Tic-T ac-Toe view must "be selectable." 10.5 '53 154 PENPOINT APPLICATION WRITING GUIDE "" How Selection Works There can only be one primary selection in the Notebook UI at a time. The user usually selects something on-screen by tapping on it. In response to holding the selection, the selected thing is highlighted. Depending on what is selected, the user can then operate on the selection by deleting it, copying it, asking for its option sheet, and so on. 10.5.1 Try tapping in a text field or text view. PenPoint's Selection Manager keeps track of which object has the selection. However, it is up to the class implementor to support selections, to highlight the selection, and to implement whatever operations on that selection make sense. Text fields and text views support selections, but dsWin and dsObject do not. "..". Which Obiect? 10.5.1.1 Because the Tic-Tac-Toe view is the object which draws the tic-tac-toe board, it makes sense for it to track selections. The selection does not change the board contents, so the Tic-Tac-Toe data object need not care. When the user selects in the Tic-Tac-Toe view, the action selects the entire view. A more realistic class would figure out which of its squares the user selected, but the principles used by dsTttView are the same. dsTttView responds to selection messages sent by the Selection Manager. It asks theSelectionManager if it holds the selection, and if so, repaints differently to indicate this fact. "..". What Event Causes Selections? 10.5.1.2 The application developer must decide what input event causes a selection in the crossword view: the usual is a pen-up event or a pen-hold timeout. On receiving this input event, the object wishing to acquire the selection should send msgSelSetOwner to the special Selection Manager object. Since the Tic-Tac-Toe view also supports keyboard input, it also calls the routine InputSetTarget to acquire the keyboard focus. From this point on, the view receives keyboard input events, and may receive other messages intended for the selection, such as options, move, and copy. The object which has acquired the selection should highlight the selected "thing" on-screen; dsTttView draws the board in gray when it has the selection. This code is from TttViewRepaint: II Fill the dirty rect with the appropriate background. If.we hold the II selection, the appropriate background is grey, otherwise it is white. II ObjCallJmp(msgSelOwner, theSelectionManager, &owner, s, Error); if (owner == self) { DbgTttViewRepaint «"owner is self"» ObjectCall(msgDcSetBackgroundRGB, (*pData)->dc, \ (P_ARGS)sysDcRGBGray33); CHAPTER 10 / HANDLING INPUT (TIC-TAC-TOE) More on View and Data Interaction else { DbgTttViewRepaint ( (" owner is not self")) ObjectCall(rnsgDcSetBackgroundRGB, (*pData)->dc, \ (P_ARGS)sySDcRGBWhite)i Supporting Selections. 10.5.1.3 When a Tic-Tac-Toe view receives a msgPenHoldTimeout input event, clsTttView sends self msgTttViewTakeSel telling it to acquire the selection, and sends self msgWinUpdate, which forces it to repaint the entire board. (If the view supported square-by-square selection, it would convert the input event X-Y coordinates to a square location on the board). Move/Copy Protocol 10.5.1.4 The selection holder receives a variety of messages, including msgSelnYield and the move/copy protocol messages. Because clsTttView inherits from clsEmbeddedWin, it can rely on clsEmbeddedWin's default handling of many selection messages. More on View and Data Interaction Thus far the data maintained by the data object has been static; now the user can change the data. But the user interacts with the view, not the data object. It's the view that knows what characters the user entered. The view must tell the data object about the change as well as draw the new data. The natural way to do this might seem to be for the view to draw the new letter in the square, and then tell the data object about the new letter. However, this is not the view-data model. Instead, the view tells the data object about the changed letter by sending it msgTttDataSetSquare. When thedata object receives this message, it updates its state, then broadcasts msgTttDataChanged to all its observers. When the view receives msgTttDataChanged, it knows it needs to repaint the board. The advantages of this model are that the data object can remail!- in control of its data: it could reject the update message from the view, and the view would not display bad data. Also, it allows for several views to display the same data object, since if any of them updates the data object, they all are told about the change. To actually draw the new square, clsTttView dirties the rectangle of the square that changed. This also may seem odd-why not paint the square immediately with the new value when notified by the data object of the new value? But the Tic-T ac-T oe view already knows how to repaint itself, it's nice to take advantage of the batching provided by the window system's repaint algorithm. The Text subsystem is a more compelling argument for the view-data model used by Tic-Tac-Toe. Using the same kind of message flow to update text views and text data objects, Text does indeed allow several views of the same underlying object, and it has a very intelligent window repainting routine. 10.6 155 156 . PENPOINT APPLICATION WRITING GUIDE J'" Hanelwriting anel Gestures 10.7 Views inherit from clsGWin, so there is little extra work required to make clsTttView respond to input events and gestures. ". Input Event Handling 10.7.1 There is one input message in PenPoint, msgInputEvent. Within the message arguments of this is a device code that indicates the type of input event. Device codes all begin with msgKey or msgPen, which is slightly confusing because objects never receive these messages, they always receive msgInputEvent. clsTttView's msgInputEvent handler calls separate routines to handle pen events and keyboard events. The pen event handler, TttViewPenInput, determines whether or not a pen-hold timeout has occurred. If a timeout occurred, the handler takes the selection by sending msgTttViewTakeSel to self, updates its window to show selection, and then determines whether the hold followed a tap or not. If the hold followed a tap (pPen->taps > 0), the handler starts a copy operation; if it didn't follow a tap, the handler starts a move operation. If no timeout occurred, the handler allows clsTttView's ancestor to handle the message. if (MsgNurn(pArgs->devCode) == MsgNurn(rnsgPenHoldTirneout)) { P PEN DATA pPen = (P PEN DATA) (pArgs->eventData); ObjCallJrnp(rnsgTttViewTakeSel, self, pNull, s, Error); ObjCallJrnp(rnsgWinUpdate, self, pNull, s, Error); ObjCallJrnp((pPen->taps == 0) ? rnsgSelBeginMove : rnsgSelBeginCopy, self, &pArgs->xy, s, Error); s = stslnputTerrninate; else { s = ObjectCallAncestorCtx(ctx); ". Gesture Handling .10.1.2 If the pen event handler passes a pen .event to clsTttView's ancestor, the event ends up being handled by clsGWin. If clsGWin recognizes the pen event as a gesture, it sends msgGWinGesture to self. In other words, when the user draws a gesture on the Tic-Tac-Toe view, the view receives msgGWinGesture. The arguments for msgGWinGesture include the gesture in the form of a message identifier. The class for the message is clsXGesture. The number of the message encodes the actual gesture detected by clsGWin. The handler for msgGWinGesture first checks that the class of the gesture is clsXGesture (in a switch statement). If not, it lets its ancestor handle the message. If the class is clsXGesture, the handler uses a switch statement to determine the message number and acts accordingly. The ClsNum macro extracts the class number from a message; the MsgNum macro extracts the message number from a message. CHAPTER 10 I HANDLING INPUT (TIC-TAC-TOE) Handwriting and Gestures MsgHandlerWithTypes(TttViewGesture, P_GWIN_GESTURE, PP_TTT_VIEW_INST) ( STATUS OBJECT Ufdef DEBUG s; owner; { P CLS SYMBUF mb; DbgTttViewGesture(("self=Ox%lx msg=Ox%lx %s", self, pArgs->msg, ClsMsgToString(pArgs->msg,mb))) } tendif I I DEBUG switch(ClsNum(pArgs->msg)) case ClsNum(clsXGesture): switch(MsgNum(pArgs->msg)) case MsgNum(xgslTap) : ObjCallJmp(msgTttViewToggleSel, self, pNull, \ s, Error); break; case MsgNum(xgsCross) : StsJmp (TttViewGestureSetSquare (self, pArgs, tttX) , \ s, Error); break; case MsgNum(xgsCircle) : StsJmp(TttViewGestureSetSquare(self, pArgs, tttO), \ s, Error); break; case MsgNum(xgsPigtailVert): case MsgNum(xgsPigtailHorz) : StsJmp(TttViewGestureSetSquare(self, pArgs, tttBlank), \ s, Error); break; case MsgNum(xgsCheck): II Make sure there is a selection. ObjCallJmp(msgSelOwner, theSelectionManager, \ &owner, s, Error); if (owner != self) { ObjCallJmp(msgTttViewTakeSel, self, pNull, s, Error); ObjCallJmp(msgWinUpdate, self, pNull, s, Error); } II Then call the ancestor. ObjCallAncestorCtxJmp(ctx, s, Error); break; default: DbgTttViewGesture(("Letting ancestor handle gesture")) return ObjCallAncestorCtxWarn(ctx); break; default: DbgTttViewGesture (("Letting ancestor handle gesture")) return ObjCallAncestorCtxWarn(ctx); DbgTttViewGesture ( ("return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewGesture (("Error; return Ox%lx", s)) return s; 1* TttViewGesture *1 '57 · '58 PENPOINT APPLICATION WRITING GUIDE 'r Keyboard Handling dsTttView's keyboard input routine handles multi-key input, for example, when the user presses two keys at once or in rapid succession. The devi,~e code for this is msgKeyMulti, and the input evef?t data includes the number of keystrokes and an array of their values. The keyCode of a key value is a simple ASCII number. dsTttView handles X or 0 or I Space I on the keyboard. 10.7.3 Chapter II/Refining the Application (Tic-Tac-Toe) Tic-Tac.,.T oe has many of the niceties expected of a real application. Many of these enhancements are independent of the program, and could be added to Empty Application as easily as to Tic-T ac-Toe. ". Debugging 11.1 You can use DB, the PenPoint Source-level debugger, to step through code. In an object-oriented system, your objects receive many messages from outside sources, many of which you may not expect. It's useful to be able to easily track the flow of messages through your routines, and to turn this on and off while your program is running. As you've noticed if you've looked at the code, Tic-Tac-Toe has extensive support for debugging. The facilities it uses are: • Tracing messages using msgTrace • Printing debug messages using Debugf • Dumping objects' state using msgDump • Giving the Class Manager symbolic names for its objects, messages, and status values using ClsSymbolslnit. The complexity in Tic-Tac-Toe arises because it lets you turn features on and off while the program is running. ",. Tracing 11.1.1 It's very useful to have a log of what messages are coming in. You can get a message log by turning on message tracing; you can either turn it on for a class or for a single instance of that class. In DEBUG mode, TTTMBAR.C defines a debug menu which can turn tracing on or off for the various classes. All the menu items send msgTttAppChangeTracing to the application. The message argument encodes the target object to trace and whether to trace it or not: static TK_TABLE_ENTRY traceMenu[] = { ("Trace App On", msgTttAppChangeTracing, {"Trace App Off", msgTttAppChangeTracing, {"Trace View On", msgTttAppChangeTracing, ("Trace View Off", msgTttAppChangeTracing, {"Trace Data On", msgTttAppChangeTracing, ("Trace Data Off", msgTttAppChangeTracing, {pNull} }; MakeU32 (0,1) } , MakeU32 (0, 0) } , MakeU32 (1, 1) }, .MakeU32(1,O)}, MakeU32 (2, 1) } , MakeU32(2,O)}, 160 PEN POINT APPLICATION WRITING GUIDE The TttDbgChangeTracing routine is implemented in TTTDBG.C. It simply sends msgTrace to the target objec~t with an argument of true or false. ".. Debugf Statements and Debug Flags Going beyond message tracing, it's useful to print out what your application is doing at various stages. One approach is to add simple Debugf statements as you debug various sections. However, in a large program you quickly get overwhelmed by debugging statements you're not interested in. Tic-Tac-Toe leaves all the Debugf statements in the code, and controls which statements show up by examining a debugging flag set. It uses DbgFlagGet to check whether a flag is set, the same as Empty App and the other simpler applications. What Tic-T ac-T oe prQvides is an easy way to print out a string identifying the routine, followed by whatever printf-style parameters you want to use. Thus this code: if (s == stsFSNodeNotFound) { DbgTttAppCheckStationery (("file not found; s=Ox%lx", s)) goto NormalExiti will print out TttAppCheckStationery: file not found; s=Oxnum but only if the appropriate debugging flag is set. So, how is it implemented? A definition of its debug routine precedes each function for which you want to print debugging information, for example, DbgTttAppCheckStationery. #define DbgTttAppCheckStationery(x) \ TttDbgHelper("TttAppCheckStationery",tttAppDbgSet,Oxl,x) Call this macro anywhere that you might want to display a debugging string. The parameter to the macro (x) is the printf-style format string and any arguments «"file not found; s=Ox%lx",s)). In order to treat multiple parameters as one, they must be enclosed in a second set of parentheses. The TttDbgHelper routine checks if the specified flag (Ox0001) is set in the specified debugging flag set (tttAppDbgSet), and if so prints the identifying string ("TttAppCheckStationery") together with any printf-style format string passed in (x). There are 256 debugging flag sets, each of which has a 32-bit value. GO uses some of them for its applications-see \PENPOINT\SDK\INC\DEBUG.H for a full list. TTTPRIV.H defines the debugging flag sets used in Tic-Tac-Toe, such as tttAppDbgSet: 11.1.2 CHAPTER 11 I REFINING THE APPLICATION (TIC-TAC-TOE) '161 Debugging II II Debug flag sets II idefine tttAppDbgSet idefine tttDataDbgSet idefine tttUtilDbgSet idefine tttViewDbgSet idefine tttViewOptsDbgSet idefine tttViewXferDbgSet OxCO OxCl OxC2 OxC3 OxC4 Oxcs Other routines use other flags. In case you care, here's the definition ofTttDbgHelper: idefine TttDbgHelper(str,set,flag,x) \ Dbg (if (DbgFlagGet ( (set) , (U32) (flag) » {DPrintf ("%s: ", str) i Debugf Xi}) Dprintf is the same as Debugf, except that Dprintf doesn't insert an automatic new-line at the end of the function. ".. Dumping Obiects 11.1.3 One of the messages defined by the Class Manager is msgDump. A class should respond to it by calling its ancestor, then printing out information about seWs state. Most classes only implement msgDump in the DEBUG version of their code. Tic-Tac-Toe lets you dump its various objects from its Debug menu. In TITMBAR.C, it defines the menu: static TK_TABLE_ENTRY debugMenu[] = { {"Dump View", msgTttAppDumpView, {"Dump Data", msgTttAppDumpDataObject, {"Dump App", msgDump, {"Dump Window Tree", (U32) dumpTreeMenu, {"Trace", (U32)traceMenu, {"Force Repaint", msgTttAppForceRepaint, {pNull} O} , OJ, O} , 0, 0, tkMenuPullRight}, 0, 0, tkMenuPullRight I tkBorderEdgeTop}, 0, 0, tkBorderEdgeTop}, }; Another approach is to have a generic dump-object function in the DEBUG version of your code which sends msgDump to its argument. When running DB, you can call this routine directly, passing it the UID of the object you want dumped. ~ u :::; II. II. 0( IU :z: .... ~ The client of the menu is the application, so to dump the application all the menu item needs to do is send msgDump. For the view and data object, you would either have to change the -clients of the menu items, or have the application class respond to special msgTttAppDumpView or insgTttAppDumpData messages by sending msgDump to the appropriate target. Tic-T ac-T oe does the latter; the handlers for these messages are in TTTDBG.C. ".,. Dumping Any Obiect z o 11.1.3.1 162 PENPO.INT APPLICATION WRITING GUIDE ". Symbol Names 11.1.4 All the Class Manager's macros (ObjCallRet, ObjCallWarn, ObjCallAncestorChk, and so on) print a string giving the message, object, and status value if they fail (in DEBUG mode). You can also ask DB to print out messages, objects, and status values. Ordinarily the most the Class Manager and DB can do is print the various fields in the UID, such as the administrated field and message number. However, if you supply the Class Manager a mapping from symbol names to English names, it and DB will use the English names in their debugging output. The Class Manager routine you use is ClsSymbolsInit. The routine takes three arrays, one for objects, one for messages, and one for status values. Each array is composed of symbol-string pairs. Tic-Tac-Toe sets up these arrays in the file S_TTT.C: const CLS_SYM_STS tttStsSymbols[] 0, O}; const CLS_SYM_MSG tttMsgSymbols[] msgTttAppChangeDebugFlag, "msgTttAppChangeDebugFlag", msgTttAppChangeDebugSet, "msgTttAppChangeDebugSet", msgTttViewTakeSel, "msgTttViewTakeSel", 0, O}; const CLS_SYM_OBJ tttObjSymbols[] cl·sTttApp, "clsTttApp", clsTttData, "clsTttData", clsTttView, "clsTttView", = { 0, O}; (Tic-Tac-Toe doesn't define any STATUS values.) ClsSymbolsInit also takes a fourth parameter, a unique string identifYing this group of symbolic names. Here's the routine in S_TTT.C that calls ClsSymbolsInit: return ClsSymbolslnit( "ttt", tttObjSymbols, t ttMsgSymbols, tttStsSymbols) ; At installation (from process instance 0), TTT.EXE calls TttSymbolsInit to load these arrays. To save space, all of this code is excluded with an hfdef if DEBUG is not set. 11.1.4.1 ""'" Generating Symbols Automatica~ly It's cumbersome to type in and update the arrays of UID-string pairs. At GO we have developed scripts that automatically generate files like S_TTT.C. These scripts require the MKS toolkit and other third-party utilities, so they are not part of the SDK release and are not supported. If you're interested in these scripts, contact GO Developer Support. CHAPTER 11 I REFINING THE APPLICATION (TlC-TAC-TOE) 163 Stationery' ~V' C'i"iii~iiig §ymb@~ Names YClUJll'se!f 11.1.4.2 Tic-Tac-Toe just prints UIDs as long integers when it needs to print them out. You can also print them in hexadecimal format using the %p format code. If you want to print out the long names within your own code, the Class Manager defines several functions to convert objects, messages, and status values to strings, such as ClsObjectToString. J"'lnstallation Features 11.2 During installation, PenPoint automatically creates several application enhancements based on the contents of the application's installation directory: • Stationery • Help notebook documents • Quick-help for the ~pplication's windows • Application icons. The nice thing about these enhancements is that you can create and modify them separate from writing and compiling the application. In fact all of these features could have been added to Empty Application, the very simplest application. General details on application installation is covered in detail in Part 12: Installation API of the PenPoint Architectural Reference. This section only covers what Tic-T ac-T oe does. J'" Stationery The user can pick a tic-tac-toe board to start with from a list of stationery. The user can draw a caret j\ over the table of contents to pop up a stationery menu, or can open the Stationery auxiliary notebook. 11.3 z o ~ u :::i II. II. ...:ccC I- ~ 164 PENPOINT APPLICATION WRITING GUIDE Figure 11-1 Stationery Notebook & Stationery Menu ".. Creating Stationery 11.3.1 The Installer looks for stationery in a subdirectory called STATNRY. Each stationery document should be in a separate directory in STATNRY. You can stamp the directories with long PenPoint names. You can also stamp the directories with attributes indicating whether the stationery should appear in the stationery menu and whether it should appear in the Stationery notebook. Here's what the Tic-Tac-Toe makefile does: stamp \penpoint\app\ttt\statnry /g "Tic-Tac-Toe (filled)" /d tttstatl /a 00800274 1 stamp \penpoint\app\ttt\statnry /g "Tic-Tac-Toe (X's)" /d tttstat2 This gives each stationery document a different name, and marks one of them for inclusion in th~ stationery menu. CHAPTER 11 / REFINING THE APPLICATION (TlC-TAC-TOE) Help Notebook ~. How 1ic-1ac-ioe Hand~es §i'Cii'Y@i'iei"Y 165 11.3.2 Stationery directories can contain a filed document-a regular instance of the application. To build such stationery you copy a document from the Notebook to the installation volume. One disadvantage of this is that it could make the stationery take up more space, since it's an entire filed document. Instead, clsTttApp always checks for a file called TTTSTUFF.TXT in the document's directory when a document is first run (during msgApplnit). The routine is TttAppCheckStationery in TTTAPP.C. If it finds a TTTSTUFF.TXT file, clsTttApp opens it and sends msgTttDataRead to its data object. This tells the data object to set its state from the file. clsTttData simply reads the first nine bytes of the file and sets its value from those; for example, the TTTSTUFF.TXT file for "Tic-Tac-Toe (filled)" (in \PENPOINT\APP\TTT\STATNRy\STATl) is simply xoxoxoxox stationery for tttapp This saves a lot of space over a filed Tic-Tac-Toe document; however, note that this form of stationery doesn't include things like the thickness of the grid in the view. The user can always make stationery that is a full document by moving or copying a Tic-Tac-Toe document to the Stationery notebook. The makefile for Tic-Tac-Toe creates the STATNRY directory in \PENPOINT\APP\TTT, and then creates the two directories STATl and STAT2. The makefile copies the file FILLED.TXT to STATl and names it TTTSTUFF.TXT; it then copies the file XSONLY.TXT to STAT2 and also names it TTTSTUFF.TXT ,.,. Help Nolebook 11.4 ~ u :::; a. a. win.flags.input 1= inputHoldTimeout; pArgs->gWin.helpld = tagTttQHelp~orView; pArgs->view.createDataObject = true; \ This is the only thing clsTttView must do to handle quick help. ".. Creating Quick Help Resources 11.5.1 One way to create resources is to tell a resource file to file an object, using say msgResPutObject. This is what happens when an application is told to save a document. However, one goal of resources is to separate the definition of a resource from the application that uses it. So you can also compile resources under DOS, putting them in a resource file, and read them from within PenPoint applications. These resources aren't objects, they are basically predefined data structures. In the case of Quick Help, a quick help resource consists of three parts: • The strings that contain the quick help text. • A tagged string array resource (type RC_TAGGED_STRING) that associates each text string with a tag. The tags are used by the GWin help Ids to associate a GWin with its quick help text. • An RC_INPUT structure containing: • A list resource ID created from the administered portion of the quick help ID (in this cas clsTttView) and the quick help group (usually resGrpQhe1p) • A pointer to the tagged string array resource for the class • A length field (updated by the resource compiler) • The identifer for the string array resource agent (resTaggedStringArrayResAgent). Each quick help string has two parts, which are separated by two vertical line characters (II). The first part is the title for the quick help card; the second part is the quick help texc The vertical line characters are not printed when quick help displays. This is the quick help string for the Tic-Tac-Toe view, defined in TTTQHELP.RC: II II II Quick Help string for the view. static CHAR tttViewString[] = { II Title for the quick help window "Tic-Tac-Toe II" II Quick help text "The Tic-Tac-Toe window lets you to make X's and O's in a Tic-Tac-Toe " z o ~ u :::::i II. II. C YO ...:z: ~ 168 PENPOINT APPLICATION WRITING GUIDE "grid. You can write X's and O's and make move, copy" "and pigtail delete gestures.\n\n" "It does not recognize a completed game, either tied or won.\n\n" "To clear the game and start again, tap Select All in the Edit mp.nu, " "then tap Delete." }i This is the RC_TAGGED_STRING resource for tttView and its option card: II Define the quick help resource for the view. static P RC TAGGED_STRING tttViewQHelpStrings[] = tagCardLineThickness, tttOptionString, tagTttQHelpForLineCtrl, tttLineThicknessString, tagTttQHelpForView, tttViewString, pNull }i This is the RC_INPUT structure for the tttView quick help: static RC_INPUT tttViewHelp = { MakeListResId(clsTttView, resGrpQhelp, 0), tttViewQHelpStrings, II Name of the string array 0, II Use string array resource agent resTaggedStringArrayResAgent }i See Part 11: Resources, in the PenPointArchitectural Reference, for more information on resource compiling and the specifics of quick help resources. To compile resource definitions into a resource file, you use the PenPoint Resource Compiler (\PENPOINT\SDK\UTIL\DOS\RC). The Installer copies the application resource file (APP.RES) during installation. Hence the makefile tells the resource compiler to append the quick help resources toAPP.RES. ~ Standard Message Facility The PenPoint standard message facility (StdMsg) provides a standard way for your application to display modal dialog boxes, error messages, and progress notes without requiring it to create UI objects. StdMsg uses clsNote (see NOTE.H) to display its messages. Notes have a title, a message body, and zero or more command buttons at the bottom. Message text and command button definitions are stored in resource files. StdMsg supports parameter substitution for the message text and button labels (see CMPSTEXT.H). A 32-bit value (a tag in the case of dialog boxes and a status code in the case of errors) is used to select the appropriate resource. StdMsg provides the following routines for when the programmer knows exactly which message is to be displayed: • System and application dialog boxes use StdMsg(tag, ... ) • Application errors use StdError(status, .,.) • System errors use StdSystemError(status, ... ) • Progress notes use StdProgressUp(tag, &token, ... ) 11.6 CHAPTER 11 / REFINING THE APPLICATION (TIC-TAC-TOE) Standard Message Facility 169 StdMsg(}, Std£rrorO, StdSystemErtorO, StdProgressUpO are varArgs functions. Any parameter substitutions are supplied with the argument list, much like printf. Like printf, there is no error checking regarding the number and type of the substitution parameters. The first three functions return an integer, which indicates the command button that the user tapped. Progress notes, which use StdProgressUpO, don't have a command bar. StdMsg also provides support for the situation where an unknown error status is encountered: StdUnknownErrorO. This function does not provide parameter substitution or multiple command buttons, it always displays a single "OK" command button. StdUnknownError(} replaces any parameter substition specifications in the text with "???". ",. Using StdMsg Facilities 11.6.1 To use StdMsg, you first define the message text strings. These strings are held in string array resources, like quick help. A single resource holds all the' ~trings for a given class. There is a separate string array for dialog boxes and error messages. You should store the application message resources in the application's APP.RES file. Here's an example of a typical resource file definition: static P STRING dialogClsFoo[] = { "This is-the first dialog message.", @CP = "[Go] [Stop] This is the second dialog message. str: "ls", }; static P_STRING errorClsFoo[] = { "This is the first error message.", @CP = "[Retry] [Cancel] This is the second error message. count: "ld" , }; static RC_INPUT dialogTabClsFoo = { resForStdMsgDialog (clsFoo.), dialogClsFoo, 0, @CP = resStringArrayResAgent }; static RC INPUT errorTabClsFoo = resForStdMsgError(clsFoO), errorClsFoo, 0, @CP = resStringArrayResAgent }; P_RC_INPUT resInput [] = { &dialogTabClsFoo, &errorT~bClsFoo, @CP = pNull }; You must define a tag.or error status for each string. The string's position in the string array determines its tag or status index (starting from 0). Here are the definitions for the example above: II fdefine tagFooDialog1 MakeDialogTag(clsFoo, 1) II *define stsFooError1 MakeStatus(clsFoo, 1) MakeDialogTag(clsFoo, 0) II @CP = *define tagFooDialog2 MakeStatus(clsFoo, 0) II @CP To create a note from the items defined above, simply call StdMsgO or StdError(}. For example: ' = fdefine stsFooError2 170 PEN POINT APPLICATION WRITING GUIDE buttonHit = StdMsg(tagFooDialog2, "String"); s = ObjectCall( ... ); i f (s stsOK) { if (s == stsFooErrorl) (StdError(stsFooErrorl); } else (StdUnknownError(s); } } Progress notes are slightly different from the message functions. Your application displays a progress notes when it begins a lengthy operation, and takes the note down when the operation completes. PenPoint 1.0 does not support cancellation of the operation. Here's an example of progress note usage: SP TOKEN token; StdProgressUp(tagFooProgressl, &token, pararnl, pararn2); . .. Lengthy operation ... StdProgressDown(&token); ",. Substituting Text and Defining Buttons 11.6.2 The message strings can contain substituted text and definitions for buttons. String substitution follows the rules defined by the compose text function (defined in CMPSTXT.H). A button definition is a substring enclosed in square brackets at the beginning of the message string. You can define any number of buttons, but you must define all buttons at the beginning of the string. The button substrings can contain text substitution. If the string doesn't define any buttons, StdMsg creates a single "OK" button. StdMsgO, StdErrorO, and StdSystemErrorO return the button number that the user tapped when dismissing the note. Button numbers start with o. For example, this string definition would result in a return value of 1 if the user tapped Buttonl: "[ButtonO] [Buttonl] [Button2] Here's your message!" Be aware that these functions might also return a negative error status, which indicates that a problem occurred inside the function. You can break your message up into paragraphs by putting two newline characters at the paragraph breaks. For example: "Here's the first paragraph.\n\nHere's the second one." ",. StdMsg and Resource Files or Lists There are variations of StdMsgO and StdErrorO that allow you to specify the resource file handle or resource list to use. These are most useful for PenPoint Services, where there is no default resource list available. These messages are: • StdMsgRes(resFile, tag, ... ) • StdErrorRes(resFile, status, ... ) 11.6.3 CHAPTER 11 I REFINING THE APPLICATION (TIC-TAC-TOE) Bitmaps (Icons) ~,.. Sl'~Msg Cusl'omi:ztil'iori fUriCl'iori 171 11.6.4 The function StdMsgCustomO allows you to customize a StdMsg note. The function returns the UID of the note object (created by clsNote), without displaying it. You can modify this object as you wish and then display it yourself using the messages defined by clsNote. ".. Bitmaps (Icons) 11.7 PenPoint uses icons to represent applications in the table of contents and in browsers. You can also use icons in your own applications. In PenPoint terminology, the icon includes optional text as well as a bitmap picture. There are default bitmaps for applications and documents, but you can create your own using the bitmap editor. Figure 11-3 ApplicatiGn and DGcument ICGns z o ~ u ::i IlL IlL ...:z: c( .... II When it needs a bitmap, the Application Framework searches for it by resource ID in your application's resource list. To begin with, your application's resource file (APP.RES) doesn't have the resource, so the search gets the default bitmap in 172 PENPOINT APPLICATION WRITING GUIDE the system resource file. However, if you put a different icon in your application's resource file, it will be' used instead. You don't need to make any changes to your application to support this. ".. Creating Icons The bitmap editor application is available in the PC \PENPOINT\APP\BITMAP directory. It is also available on the PenPoint Goodies disk. The bitmap editor needs to generate a bitmap as a resource and put it in a resource file. However, it conforms to the Pen Point document model, so it has no Save command. Instead, you use the About... menu item in the Document menu to bring up the Export option card to specify the type of bitmap resource, and then use the Export... command to actually generate the bitmap resource. You generally export four bitmaps, two each for the application and document in 16x16 and 32x32 sizes. Although you can export them to the APP.RES file your application's installation directory, it is often preferable to create separate SMICON.RES and LGICON.RES files for the large and small icons. For more information on using the bitmap editor, see Part 3: Tools in PenPoint Development Tools. 11.7.1 Chapter 12 / Releasing the Application You're almost done, but not quite. Before you make your application available to the larger world of PenPoint users, you must complete these tasks: • Register your classes with GO • Document the application • Prepare your distribution disks .. You should also consider making your classes available to other developers. If you do so, you need to document the API for those classes. ~Registering Your Classes 12.1 While developing an application, you can identify your classes with the . well-known UIDs wknGDTa through wknGDTg. Of course, if you use these well-known UIDs in a published application, they will conflict with other developers who use your application and attempt to use these well-known UIDs to test their own applications. When you are fairly sure that you will publish your application, you must contact GO Developer Technical Support (preferably through electronic mail) for an administered object value for each of your public classes. Remember that aUlD consists of an administered object value, a version number, and a scope (global or local, well-known or private). ~ Documenting the Application 12.2 The need for quality documentation cannot be over-emphasized. There are three ways in which you should document your application: • Manuals or other form of separate documentation • Pages in the Help notebook • Quick Help text. ". Writing Manuals For more information on documenting your application, contact GO Developer Technical Support and ask for Tech Note #8, Documenting PenPoint Applications. This technical note, written by GO's end-user documentation group, provides information about how GO writes and produces its end-user documentation. The tech note also give print specifications, if you want your documentation to appear similar to GO's. 12.2,1 174 PENPOINT APPLICATION WRITING GUIDE ".. Screen Shots 12.2.2 The S-Shot utility enables you to capture TIFF images of Pen Point computer screens. You can then incorporate your images into your documentation. S-Shot is on the SDK Goodies disk. ".. Gesture Font 12.2.3 For developer and end-user documentation, GO created an Adobe Type 1 font that depicts the PenPoint gesture set. If you use a PostScript printer, you can incorporate this font into your documentation and on-line help. Registered developers may request a copy of the PenPoint Gesture font from GO by contacting PenPoint Developer Tech Support. ". On-Disk Structure 12.3 When developing your application, the PenPoint file organization requires you to place your files in certain specific directories under the \PENPOINT directory. This is described in detail in Chapter 3 of Part 12: Installation API in the PenPoint Architectural Reference. Before distributing your application, you should ensure that all your auxiliary files, such as Help notebook pages, stationery, resource files, and so on are in their correct directories. ". Sharing Your Classes If you have created a component class that might be useful to other PenPoint developers, you should consider licensing the class. 12,4 Appendix Sample Code This appendix lists the sample code referred to in the preceeding chapters of this book. The subdirectories in \PENPOINT\SOK\SAMPLE contain the complete source files for these and other sample programs . . The sample code for these applications is listed in this appendix: Empty App The most simple application you can create. Hello World (Toolkit) A simple Hello World application that uses the VI Toolkit. Hello World (Custom Wmdow) A simple Hello World application that uses the Image Point graphics subsystem. Counter Application A simple application that saves and restores its data. Tic-Tac-Toe A fully featured PenPoint application. Template Application A template for a fully-featured PenPoint application. This appendix describes, but does not list, the sample code for: Adder A simple pen-centric calculator, limited to addition and subtraction Calculator A floating, button~operated calculator. Clock A digital alarm clock accessory. Notepaper App A notetaking application that uses the NotePaper DLL. Paint A simple raster painting program. Toolkit Demo Shows how to use many of the classes in the VI toolkit. Input Application Demonstrates pen-based input event handling. Writer Application Demonstrates handwriting translation. Basic Service Contains the absolute minimum code required for a service. Test Service Provides a starter kit for most service writers. MIL Service Provides a starter kit for device driver writers. Empty Application 177 Hello World (foolkit) 180 Hello World (Custom Wmdow) 187 Counter Application 195 Tic-Tac-Toe 204 Template Application 251 Adder 260 Calculator 261 Clock 263 Notepaper App 265 Paint 266 Toolkit Demo 267 Inputapp 269 Writerap ·271 Basic Service 272 Test Service 272 MIL Service 273 Empty Application Obiectives EmptyApp is used in the Application Writing Guide to show how to compile, install, and run applications. Empty Application is the simplest sample application distributed with the PenPoint Software Developer's Kit. It does not have a view or any data. The only behavior it adds to the default PenPoint application is to print out a debugging message when the application is destroyed. This sample application also shows how to: • Use Debugf and #ifdefDEBUG/#endif pairs • Turn on message tracing for a class • Let the PenPoint Application Framework provide default behavior. Class Overview Empty Application defines one class: clsEmptyApp. It makes use of the following classes: clsApp clsAppMgr Compiling To compile EmptyApp, just cd \penpoint\sdk\sample\emptyapp wmake I- This compiles the application and creates EMPTYAPP.EXE in \PENPOINT\APP\EMPTYAPP. I Running I After compiling EmptyApp, you can run it by To provide this behavior, EmptyApp defines clsEmptyApp, which inherits from clsApp. In its handler for msgDestroy, clsEmptyApp prints out a simple debugging message. clsEmptyApp inherits a rich set of default functionality from clsApp. When using EmptyApp, you can create, open, float, zoom, close, rename, embed, and destroy EmptyApp documents. 1 Adding \\boot\penpoint\app\Empty Application to \PENPOINT\BOOT\APP .INI 2 Booting PenPoint 3 Creating a new Empty Application document, and turning to it. Alternatively, you can boot PenPoint and then install Empty Application through the Connections Notebook. files Used The code for Empty Application is in \PENPOINT\SDK\SAMPLE\EMPTYAPP. The files are: EMPTY APPLICATION I SAMPLE CODE ...:::; ..... EMPlYAPP.C the application class's code and initialization. METHODS.TBL the list of messages that the application class responds to, and the associated message handlers to call METHODS.TIL 1**************************************************************************** File: methods.tbl Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.0 $ 07 Jan 1992 16:37:36 $ $Date: classes.tbl contains the method table for clsEmptyApp. ****************************************************************************1 #ifndef CLSMGR_INCLUDED #include #endif MSG_INFO clsEmptyAppMethods [] #ifdef DEBUG msgDestroy, "EmptyAppDestroy", #endif objCallAncestorAfter, II for application messages (and clsmgr.h) II for debugging statements. II for AppMgr startup stuff II II method function prototypes generated by MT for strcpy(). 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * STATUS EXPORTED EmptyAppInit (void); *define clsEmptyApp wknGDTa }; o ***************************************************************************1 Hfndef APP .INCLUDED #include #endif #ifndef DEBUG INCLUDED #include #endif #ifndef APPMGR INCLUDED #include #endif #include #include * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 o CLASS_INFO classlnfo[] = "clsEmptyAppTable", FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ $Date: 07 Jan 1992 16:37:22 $ This file contains just about the simplest possible application. It does not have a window. It does not have any state it needs to save. This class does respond to a single message, so it has a separate method table and a method to handle that message. All the method does is print out a debugging string. If you turn on the "F1" debugging flag (e.g. by putting DEBUGSET=/DFOOOI in \penpoint\boot\environ.ini), then messages to clsEmptyApp will be traced. plsEmptyAppMethods, 0, }; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 EMPTYAPP.C 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1**************************************************************************** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** File: emptyapp.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU Message Handlers * * EmptyAppDestroy Respond to msgDestroy by printing a simple message if in DEBUG mode. ****************************************************************************1 MsgHandler(EmptyAppDestroy) { • Ufdef DEBUG Debugf ("EmptyApp: app instance %p about to die!", self); #endif return stsOK; Error: return s; } 1* ClsEmptyApplnit *1 II II The Class Manager will pass the message onto the ancestor II if we return a non-error status value. II return stsOK; MsgHandlerParametersNoWarning; } 1* EmptyAppDestroy *1 1***************************************************** *********************** main Main application entry point (as a PROCESS -- the app's MsgProc is where messages show up once an instance is running) . II suppress compiler warnings ****************************************************************************/ 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Installation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** ClsEmptyApplnit Install the EmptyApp application class as a well-known UID. *****************************~************************ *~********************I STATUS ClsEmptyApplnit (void) void CDECL main ( int char * U16 Dbg (Debugf ("main: starting emptyapp.exe [%d]", processCount);) if (processCount == 0) ( II Create application class. ClsEmptyApplnit(); I I Invoke app monitor to install this application. AppMonitorMain(clsEmptyApp, objNull); else ( II Create an application instance and dispatch messages. AppMain(); ( APP_MGR_NEW new; STATUS s; II II Install the Empty App class as a descendant of clsApp. II ObjCaIIRet(msgNewDefaults, clsAppMgr, &new, s); new.object.uid clsEmptyApp; new.object.key (OBJ_KEY)clsEmptyAppTable; new.cls.pMsg clsEmptyAppTable; clsApp; new.cls.ancestor II II This class has no instance data, so its size is zero. II = Nil (SIZEOF) ; new.cls.size II II This class has no msgNew arguments of its own. II new. cIs. newArgsSize = SizeOf (APP _NEW) ; new.appMgr.flags.accessory = true; strcpy (new.appMgr. company, "GO Corporation"); strcpy(new.appMgr.defaultDocName, "Empty App Document"); ObjCaIIJmp(msgNew, clsAppMgr, &new, s, Error); II II Turn on message tracing if flag is set. II if (DbgFlagGet ('F', OxlL)) ( Debugf("Turning on message tracing for clsEmptyApp"); (void) ObjCallWarn (msgTrace, clsEmptyApp, (P_ARGS) true); argc, argv[] , processCount) } II Suppress compiler's "unused parameter" warnings Unused(argc); Unused(argv); } 1* main *1 MAKEFILE ############################################### # # WMake Makefile for EmptyApp # # Copyright 1990-1992 GO Corporation. All Rights Reserved. # # You may use this Sample Code any way you please provided you # do not resell the code and that this notice (including the above # copyright notice) is reproduced on all copies. THIS SAMPLE CODE # IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION # EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT # LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU # FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF # THE USE OR INABILITY TO USE THIS SAMPLE CODE. # # $Revision: 1.3 $ EMPTY APPLICATION I ;:; 10 SAMPLE CODE .. CD * $Date: 07 Jan 1992 16:37:30 Hello World 'Toolkit) $ * *****,*******************************,********* * Set PENPOINT_PATH to your environment variable, if it exists. set it to \penpoint !ifdef %PENPOINT PATH PENPOINT_PATH = $(%PENPOINT_PATH) !else PENPOINT PATH = \penpoint !endif The DOS name of your project directory PROJ = emptyapp Standard defines for sample code !INCLUDE $ (PENPOINT_PATH)\sdk\sample\sdefines.mif The PenPoint name of your application EXE_NAME = Empty Application The linker name for your executable: company-name-V «minor» EXE_LNAME= GO-EMPTYAPP_EXE-Vl(O) Object files needed to build your app EXE_OBJS = methods.obj emptyapp.obj Libs needed to build your app EXE_LIBS = penpoint app Targets all: $(APP_DIR)\$(PROJ).exe . SYMBOLIC The clean rule must be :: because it is also defined in srules clean :: .SYMBOLIC -del methods.h Dependencies emptyapp.obj: emptyapp.c methods.h Standard rules for sample code !INCLUDE $(PENPOINT_PATH)\sdk\sample\srules.mif One of the simplest applications in any programming environment is one that prints the string "Hello World." *Otherwise, * * * * * * * * * * Because Pen Point provides both an API to the ImagePoint imaging model and a rich collection of classes built on top ofImagePoint, there are two different approaches to building a "Hello W orId" application. They are: • Create a window and draw text in it using ImagePoint calls • Use PenPoint's UI Toolkit classes to create a label object. 0 Each of these approaches is worth demonstrating in a sample application. The first is a good example for programs that need to do a lot of their own drawing, such as freeform graphics editors. The second approach shows how easy it is to use the toolkit classes, and serves as an example for programs that need to draw forms or other structured collections of information. Therefore, there are two "Hello World" sample applications: Hello World (custom window) and Hello World (toolkit). The rest of this document describes Hello World (toolkit). Hello World (toolkit) uses dsLabel to display "Hello World" in a window. The simplest way of doing this is to make a single label, which also serves as the window for the application. The code for doing so is inHELLOTK1.C. Since developers will typically want to display more than one toolkit class in a window, we created a second file, HELLOTK2.C. This file shows how to create a layout object (a window with knowledge of how to layout toolkit objects) and a label which is inserted into the layout object. To change between these two source code files, simply copy the version you want to run to HELLOTKC before compiling the application. (See "Compiling" below for more detailed instructions.) cd \penpoint\sdk\sample\hellotk Next, make the version of the application that you want to test: copy HELLOTK1.C HELLOTK.C wmake or copy HELLOTK2.C HELLOTK.C wmake This compiles the application and creates HELLOTKEXE in \PENPOINT\APP\HELLOTK Running After compiling Hello World (toolkit), you can run it by 1 Adding \\boot\penpoint\app\Hello World (toolkit) to \PENPOINT\BOOT\APP .INI 2 Booting PenPoint 3 Creating a new Hello World (toolkit) document, and turning to it. Alternatively, you can boot PenPoint and then install Hello World (toolkit) through the Connections Notebook. Obiectives This sample application shows how to: lesting • Use clsLabel Zoom the document, or resize a floating document. • Create a custom layout window. Files Used Class Overview Hello World (toolkit) defines one class: clsHelloWorld. It makes use of the following classes: dsApp The code for Hello World (toolkit) is in \PENPOINT\SDK\SAMPLE\HELLOTK The files are: HELLOTK.C source code (actually a copy of either hellotkl.c or hellotk2.c) which is compiled by the makefile HELLOTKl.C source code for making a single label, which also serves as the window for the application clsAppMgr clsCustomLayout HELLOTK2.C source code for making a layout object and inserting a label in clsLabel it METHODS.TBL the method table for dsHelloWorld. Compiling To compile Hello World (toolkit), just HELLO WORLD (TOOLKIT) • SAMPLE CODE .. ; METHODS.TBL 1**************************************************************************** File: methods.tbl Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1. 0 $ $Date: 07 Jan 1992 16:47:30 $ Methods.tbl contains the method table for clsHelloWorld (toolkit version). ... lot $Date: 08 Jan 1992 11:41:14 $ This file contains the application class for a "Hello World" application using toolkit components. This uses the PenPoint UI Toolkit to draw in its window -- thus it does not create a window class. It creates a label as its client window in response to msgAppInit. It has dummy message handlers for msgAppClose and msgAppOpen so it can share the same methods.tbl with hellotk2.c. It does not have any state it needs to save. It does not have any instance data. Most applications have more than one window in their frame. hellotk2.c is an alternative version of hellotk.c which creates a label inside a custom layout window. I f you turp on the "F20" debugging flag (e.g. by putting DEBUGSET=/F0020 in \penpoint\boot\environ.ini), then messages to clsHelloWorld will be traced. *****************************************************************************1 ***************************************************************************1 II II II #ifndef DEBUG_INCLUDED #include #endif #ifndef CLSMGR_INCLUDED #include #endif #ifndef APP_INCLUDED #include #endif #ifndef APPMGR_INCLUDED #include #endif #ifndef _STRING_H_INCLUDED #include #endif #ifndef LABEL_INCLUDED #include #endif #ifndef FRAME_INCLUDED #include #endif #include #define clsHelloWorldwknGDTb Include files #include MSG INFO clsHelloMethods [] msgAppInit, msgAppOpen, msgAppClose, "HelloAppInit", objCallAncestorBefore, "HelloOpen", objCallAncestorAfter, "HelloClose", objCallAncestorBefore, o I; CLASS_INFO classInfo[] "clsHelloTable", clsHelloMethods, 0, o I; HELLOTK1.C 1**************************************************************************** File: hellotk1.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.5 $ (from hello.c 1.1) II II II II for debugging statements. II for O-OP' support. for application messages (and clsmgr.h) II for AppMgr startup stuff II for strcpy(). II for label. II for frame metrics. method function prototypes generated by MT avoids clashing with other HelloWorlds 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Methods * * * * * * * * * * * * ~ * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** HelloAppInit Respond to msgAppInit by creating the client window (a label) . ****************************************************************************1 MsgHandler(HelloAppInit) { APP METRICS am; LABEL NEW In; STATUS s; Dbg (Debugf (" HelloTK: Create the client Win");) II Create the Hello label window. ObjCallWarn(msgNewDefaults, clsLabel, &In); In.label.style.scaleUnits = bsUnitsFitWindowProper; In.label.style.xAlignment lsAlignCenter; In.label.style.yAlignment = lsAlignCenter; In. label. pSt ring = "Hello World!"; ObjCallRet(msgNew, clsLabel, &In, s); II Get the app's main window (its frame). ObjCallJmp(msgAppGetMetrics, self, &am, s, error); II Insert the label in the frame as its client window. ObjCallJmp(msgFrameSetClientWin, am.mainWin, \ (P_ARGS)ln.object.uid, s, error); return stsOK; MsgHandlerParametersNoWarning; error: ObjCallWarn(msgDestroy, In.object.uid, Nil(OBJ_KEY)); return S; } 1* HelloAppInit *1 I*********~******************************************* *********************** HelloOpen Respond to msgAppOpen by creating UI objects that aren't filed. But I create my user interface in msgAppInit, so it's filed and restored for me, so do nothing. ****************************************************** **********************1 MsgHandler(HelloOpen) { Dbg (Debugf ("HelloTK: msgAppOpen");) II When the message gets to clsApp the app will go on-screen. return stsOK; MsgHandlerParametersNoWarning; } 1* HelloOpen *1 1***************************************************** *********************** II When the message gets to its ancestor the frame will be taken II off-screen. return stsOK; MsgHandlerParametersNoWarning; } 1* HelloClose *1 1***************************************************** *********************** ClsHellolnit Install the Hello application. ****************************************************** **********************1 STATUS ClsHelloInit (void) { APP_MGR_NEW new; STATUS S; II Install the class. ObjCallWarn(msgNewDefaults, clsAppMgr, &new}; new.object.uid clsHelloWorld; (OBJ_KEY)clsHelloTable; new.object.key new.cls.pMsg clsHelloTable; new.cls.ancestor clsApp; II This class has no instance data, so its size is zero. new.cls.size Nil(SIZEOF); II This class has no msgNew arguments of its own. new.cls.newArgsSize SizeOf(APP_NEW); new.appMgr. flags. stationery true; new.appMgr.flags.accessory true; new.appMgr.flags.allowEmbedding = false; new.appMgr.flags.hotMode false; strcpy (new. appMgr. company, "GO Corporation"); ObjCallRet(msgNew, clsAppMgr, &new, s); i f (DbgFlagGet('F', Ox20L)) { Debugf("Turning on message tracing for clsHelloWorld (toolkit)"); (void) ObjCallWarn (msgTrace, clsHelloWorld, (P_ARGS) true); return stsOK; } 1* ClsHelloInit *1 1***************************************************** *********************** main Main application entry point. HelloClose ****************************************************** **********************1 Respond to msgAppClose by destroying ur objects that aren't filed. But I create my user interface in msgAppInit, so it's filed and restored for me, so do nothing. void CDECL main int char * U16 ****************************************************** **********************1 MsgHandler(HelloClose) ( Dbg(Debugf("HelloTK: msgAppClose");) argc, argv[], processCount) Dbg (Debugf ("main: starting HelloTK1. exe [%d] ", processCount);) if (processCount == 0) ( II Initialize self. HELLO WORLD (TOOLKIT) I ..• Col SAMPLE CODE ClsHellolnit () ; II· Invoke app monitor to install this application. AppMonitorMain(clsHelloWorld, objNull); else ( II Start the application. AppMain() ; Unused(argc); II Unused(argv); Suppress compiler warnings 1* main *1 HELLOTK2.C 1**************************************************************************** File: hellotk2.c Copyright 1990-i992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES; INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.6 $ (from hello.c 1.1) 08 Jan 1992 11:41:18 $ $Date: This file contains the application class for a "Hello World" application using toolkit components. This uses the PenPoint UI Toolkit to draw in its window -- thus it does not create a window class. Instead, it creates a custom layout window in its frame and inserts a label within the layout window. Other versions of hellotk.c don't use custom layout, or create several toolkit windows. It does not have any state it needs to save. It does not have any instance data. If you turn on the "F20" debugging flag (e.g. by putting DEBUGSET=/F0020 in \penpoint\boot\environ.ini), then messages to clsHelloWorld will be traced. If you turn on the "F40" debugging flag then the custom layout window will be visible (gray background, rounded border) . ***************************************************************************1 #ifndef DEBUG_INCLUDED iinclude iendif #ifndef CLSMGR_INCLUDED #include #endif II for debugging statements. II for O-OP support. #ifndef APP_INCLUDED #include fendif 'ifndef APPMGR INCLUDED finclude iendif #ifndef _STRING_H_INCLUDED 'include tendif fifndef LABEL_INCLUDED tinclude tendif #ifndef FRAME_INCLUDED ~include fendif #include tdefine clsHelloWorldwknGDTc ..•.. II for application messages II for AppMgr startup stuff II for strcpy(). II for label. II for frame metrics. (and clayout.h) II II method function prototypes generated by MT avoids clashing with other HelloWorlds 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Methods * * * * * * * * * * * * * * ** * * * * * * * * * * i * * * * * * * * * * * * *1 1**************************************************************************** HelloAppInit Respond to msgApplnit by creating long-lived objects (filed state) . But I create and destroy my user interface in msgAppOpen/msgAppClose, so do nothing. ****************************************************************************1 MsgHandler(HelloApplnit) { Dbg(Debugf ("HelloTK: msgApplnit");) II When the message gets to clsApp the frame will be created. return stsOK; MsgHandlerParametersNoWarning; } 1* HelloAppinit *1 1**************************************************************************** HelloOpen Respond tomsgAppOpen by creating the U.I.: o a custom layout window o and a label within it. ****************************************************************************1 MsgHandler(HelloOpen} ( APP_METRICS am; WIN_METRICS wm; CSTM_LAYOUT_NEW cn; CSTM_LAYOUT_CHILD_SPEC cs; LABEL_NEW In; STATUS s; Dbg{Debugf("HelloTK: Create the client Win");} ObjCaIIWarn(msgNewDefaults, clsCustomLayout, &cn); II ?? Needed?? cn.win.parent = frame; II If the frame is floating, this will make it wrap neatly II around the label. cn.border.style.leftMargin = cn.border.style.rightMargin bsMarginSmall; cn.win.flags.style 1= wsShrinkWrapHeight; if (DbgFlagGet('F', Ox40L» cn.border.style.join cn.border.style.edge cn.border.style.backgroundlnk bsJoinRound; bsEdgeAll; bsInkGray33; = = ObjCaIIRet(msgNew, clsCustomLayout, &cn, s); II Create the Hello label window. ObjCaIIWarn(msgNewDefaults, clsLabel, &In); In.label.pString = "Hello World!"; ObjCaIIJmp(msgNew, clsLabel, &In, s, errorl); II Insert the Hello win in the custom layout window. wm.parent = cn.object.uid; ObjCaIIJmp(msgWinInsert, In.object.uid, &wm, s, error2); II Specify how the custom layout window should position the label. CstmLayoutSpecInit(&(cs.metrics»; cs.child = In.object.uid; cs.metrics.x.constraint CIAlign(cICenterEdge,cISameAs, cICenterEdge); cs.metrics.y.constraint CIAlign(cICenterEdge, clSameAs, cICenterEdge); cs.metrics.w.constraint clAsIs; cs.metrics.h.constraint clAsIs; ObjCaIIJmp(msgCstmLayoutSetChildSpec, cn.object.uid, &cs, s, error2); II Get the app's main window (its frame). ObjCaIIJmp(msgAppGetMetrics, self, &am, s, error2); II Insert the custom layout window in the frame. ObjCaIIJmp(msgFrameSetClientWin, am.mainWin, \ (P_ARGS)cn.object.uid, s, error2); II When the message gets to its ancestor this will all go on-screen. return stsOK; MsgHandlerParametersNoWarning; error2: ObjCaIIWarn(msgDestroy, In.object.uid, Nil(OBJ_KEY»; errorl: ObjCaIIWarn(msgDestroy, cn.object.uid, Nil(OBJ_KEY»; return S; } 1* HelloOpen *1 1**************************************************************************** HelloClose Respond to msgAppClose by destroying the client window. I The ancestor has already taken us off-screen. ****************************************************************************1 MsgHandler(HelloClose) { APP_METRICS am; win; WIN key = objWKNKey; OBJ KEY s; STATUS II Get the client window. ObjCaIIRet(msgAppGetMetrics, self, &am, s); ObjCaIIRet(msgFrameGetClientWin, am.mainWin, (P_ARGS)&win, s); II Destroy it. ObjCaIIRet(msgDestroy, win, &key, s); Dbg(Debugf ("HelloTK: back from freeing client Win");) II Tell the app that it no longer has a client window. ObjCaIIRet(msgFrameSetClientWin, am.mainWin, (P_ARGS)objNull, s); return stsOK; MsgHandlerParametersNowarning; } 1* HelloClose *1 1**************************************************************************** CIsHellolnit Install the Hello application. ****************************************************************************1 STATUS CIsHelloInit (void) ( APPJMGR_NEW new; STATUS S; II Install the class. ObjCaIIWarn(msgNewDefaults, clsAppMgr, &new); new.object.uid clsHelloWorld; new. object. key (OBJ_KEY)clsHelloTable; new.cls.pMsg clsHelloTable; new.cls.ancestor clsApp; II This class has no instance data, so its size is zero. new.cls.size Nil(SIZEOF); II This class has no msgNew arguments of its own. new.cls.newArgsSize SizeOf(APP_NEW); new.appMgr.flags.stationery true; new.appMgr.flags.accessory true; new.appMgr.flags.allowEmbedding = false; new.appMgr.flags.hotMode false; strcpy (new. appMgr. company, "GO Corporation"); ObjCaIIRet(msgNew, clsAppMgr, &new, s); i f (DbgFlagGet('F', Ox20L» { Debugf("Turning on message tracing for clsHelloWorld (toolkit)"); (void)ObjCaIIWarn(msgTrace, clsHelloWorld, (P_ARGS) true); HELLO WORLD (TOOLKIT) ..• III SAMPLE CODE ... ' return stsOK; } 1* ClsHelloInit *1 1**************************************************************************** main Main application entry point. ****************************************************** **************~*******I void CDECL main int char * U16 argc, argv[], processCount) Dbg (Debugf ("main: starting HelloTK2.exe[%d]", processCount);) i f (processCount == 0) ( / I Initialize self. ClsHelloInit(); II Invoke app monitor to install this application. AppMonitorMain(clsHelloWorld, objNull); else { II Start the application. AppMain() ; Uriused(argc); Unused(argv); II Suppress compiler warnings 1* main *1 MAKEFILE i.i"i.",••"." •••",••,t,." ••• it,.tt.,.,••• I I I • I i • • I • I • I I • WMake Makefilefor HelloTK Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. I $Revision: • $Date: I 1.6 $ 07 Jan 1992 16:47:16 $ .",••""""""."".tt.,t.,••"".'"i" ••• I Set PENPOINT_PATH to your environment variable, if it exists. • Otherwise, set it to \penpoint !ifdef %PENPOINT_PATH PENPOINT PATH = $(%PENPOINT_PATH) !else PENPOINT_PATH = \penpoint !endif I The DOS name of your project directory. PROJ = hellotk I Standard defines for sample code (needs the PROJ) definition !INCLUDE $(PENPOINT_PATH)\sdk\sample\sdefines.mif I The PenPoint name of your application EXE_NAME = Hello World (toolkit) • The ,linker name for your executable : company-name-V ( Unclude MSG INFO clsHel1oWinMethods [1 msglnit, msgFree, . msgWinRepaint, o CLASS_INFO classlnfo[] "clsHelloWinTable" , o }; " " HELLO.C Include files objCallAncestorBefore, objCallAncestorAfter, 0, }; *****************************************************************************' " "HelloWinlnit", "HelloWinFree" , "HelloWinRepaint", clsHelloWinMethods, 0, '**************************************************************************** 'include 'include MSG INFO clsHelloWorldMethods [] "HelloOpen" , msgAppOpen, msgAppClose, "HelloClose" , o }; CLASS_INFO classlnfo[] objCallAncestorAfter, objCallAncestorBefore, File : hello . c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ..• • PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITy TO USE THIS SAMPLE CODE. $Revision: 1.4 $ 13 Nov 1991 18:04:56 $ $Date: This file contains the application class for a simple "Hello World" application. It creates an instance of clsHelloWin and inserts it in its frame. It does not have any state it needs to save. It does not have any instance data. If you turn on the "FlO" debugging flag (e.g. by putting DEBUGSET=/DFOOIO in \penpoint\boot\environ.ini), then messages to clsHelloWorld will be traced. ObjCallWarn(msgNewDefaults, 'clsHelloWin, &hwn); ObjCallRet(msgNew, clsHelloWin, &hwn, s); II Insert the Hello win in the frame. ObjCallJmp(msgFrameSetClientWin, am.mainWin, (P_ARGS)hwn.object.uid, s, exit); II Ancestor will put it all on the screen. return stsOK; MsgHandlerParametersNoWarning; II suppress compiler warnings about unused parameters exit: ObjCallWarn(msgDestroy, hwn.object.uid, pNull); return s; } 1* HelloOpen *1 ***************************************************************************1 'ifndef DEBUG INCLUDED 'include 'endif ,ifndef APP_INCLUDED iinclude 'endif ,ifndef APPMGR_INCLUDED 'include iendif 'ifndef FRAME INCLUDED 'include 'endif 'include 'include 'include 'define clsHelloWorld II for debugging statements. Respond to msgAppClose by destroying the client window. II II for application messages. II for AppMgr startup stuff II for frame metrics. II II for strcpy(). method definitions clsHelloWin's UID and msgNew args. MakeWKN(2164,1,wknGlobal) Methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** HelloOpen Respond to msgAppOpen by creating a clsHelloWin instance and inserting it as the frame's client window. ****************************************************************************1 MsgHandler(HelloOpen) ( HELLO_WIN_NEW hwn; APP METRICS am; STATUS s; II Get the app's main window (its frame). ObjCallRet(msgAppGetMetrics, self, &am, s); II Create the Hello window. I HelloClose ****************************************************************************1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * 1**************************************************************************** MsgHandler(HelloClose) ( APP METRICS am; clientWin; WIN STATUS s; II Ancestor has taken the main window (frame) off the screen. II Get the client window. ObjCallRet(msgAppGetMetrics, self, &am, s); ObjCaIIRet(msgFrameGetClientWin, am.mainWin, &clientWin, s); II Destroy it. ObjCaIIRet(msgDestroy, clientWin, objWKNKey, s); Dbg (Debugf ("Hello: back from freeing HelloWin");) II Update the frame since the client window is gone. ObjCallRet(msgFrameSetClientWin, am.mainWin, (P_ARGS)objNull, s); return stsOK; MsgHandlerParametersNoWarning; 1/ suppress compiler warnings about unused parameters } /* HelloClose */ 1**************************************************************************** ClsHellolnit Install the Hello application. ****************************************************************************/ STATUS ClsHellolnit (void) { APP MGR NEW new; STATUS s; 1/ Install the application class. HELLO WORLD (CUSTOM WINDOW) ..• 00 SAMPLE CODE .. 10 ObjCalIWarn(msgNewDefaults, clsAppMgr, 'new); new. object; uid clsHelloWorld; new.object.key (OBJ_KEY)clsHelloWorldTable; new.cls.pMsg clsHelloWorldTable; new.cls.ancestor clsApp; II This class has no instance data, so its size is zero. new. cIs. size = Nil (SIZEOF) ; II This class has no msgNew arguments of its own. new. cIs. newArgsSize SizeOf (APP NEW); new.appMgr.flags.stationery = true; - . new.appMgr.flags.accessory = true; strcpy (new.appMgr. company, "GO Corporation"); ObjCallRet(msgNew, clsAppMgr, 'new, s); if (DbgFlagGet('F', OxlOL» ( Debugf(nTurning on message tracing for clsHelloWorld"); (void) ObjCallWarn (msgTrace, clsHelloWorld, (P_ARGS) true); return stsOK; } 1* ClsHelloInit *1 1***************************************************** *********************** main Main application entry point. ****************************************************** **********************1 void CDECL main argc, int argv[], char * processCount) U16 Dbg(Debugf("main: starting Hello.exe[%d]", processCount);) if (processCount == 0) ( II II Initialize self. I! II Note that the loader calls DLLMain in the Hello World DLL, II which creates clsHelloWin. II ClsHelloInit () ; II Invoke app monitor to install this application. AppMonitorMain(clsHelloWorld, objNull); else ( II Start the application. AppMain() ; Unused(argc); } 1* main *1 Unused(argv); II Suppress compiler warnings HELLOWIN.H 1***************************************************** *********************** File: hellowin.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.3 $ 13 Nov 1991 18:04:58 $ $Date: This file contains the API definition for clsHelloWin. clsHelloWin inherits from clsWin. It has no messages or msgNew arguments. ****************************************************** **********************1 ,ifndef HELLO_WIN_INCLUDED tdefine HELLO_WIN_INCLUDED 'include tinclude MakeWKN(2165,I,wknGlobal) 'define clsHelloWin 1***************************************************** *********************** msgNew takes P_HELLO_WIN_NEW, returns STATUS category: class message Creates a new Hello Window. *1 tdefine helloWinNewFields\ winNewFields typedef struct { helloWinNewFields } HELLO_WIN_NEW, *P_HELLO_WIN_NEW; 1***************************************************** *********************** takes P_HELLO_WIN_NEW, returns STATUS msgNewDefaults category: class message Initializes HELLO WIN NEW structure to default values. *1 'endif HELLOWIN.C 1***************************************************** *********************** File: hellowin.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice} is reproduced on all copies. THIS SAMPLE CODE o IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY·CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.5 $ $Date: 13 Nov 1991 18:05:04 $ This file contains a simple "Hello World" window subclass. It creates a drawing context to paint a welcome message in self; Since clsHellOWin doesn't use the DC anywhere else but msgWinRepaint, it could create it on the fly during msgWinRepaint processing, but instead clsHelloWin saves the 'DC in its instance data. Since clsHelloWorld frees the hello window upon receiving msgAppClose, the DC doesn't take up space when the application is "closed down." The. repainting routine jumps through some geometry/drawing context hoops to ensure that the drawing fits in the window yet remains proportionately sized. If you turn on the "F40" debugging flag (e.g. by putting DEBUGSET=/DF0040 in \penpoint\boot\environ.ini), then drawing takes places with thick lines so that drawing operations are more visible. If you turn on the "F20" debugging flag, messages to clsHelloWin will be traced. ****************************************************** **********************1 tinclude tinclude tinclude tinclude tinclude II for memset(). II for scale calc. in fixed point. 'include h.nclude II method definitions #include II clsHelloWin's UID and msgNew args. typedef struct INSTANCE_DATA SYSDC dc; INSTANCE_DATA, *P_INSTANCE_DATA; II Scale font to 100 units to begin with. #define initFontScale100 II Line thickness a twelfth of the font scale. #define lineThickness8 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** HelloWinInit ****************************************************** **********************1 MsgHandler(HelloWinInit) ( SYSDC NEW dn; INSTANCE DATA data; SYSDC FONT SPEC fs; SCALE fontScale; STATUS s; II Null the instance data. memset(&data, 0, sizeof(INSTANCE_DATA»; II Create a dc. ObjCaIIWarn(msgNewDefaults, clsSysDrwCtx, &dn); ObjCallRet (msgNew, clsSysDrwCtx, ·&dn, s); data. dc = dn. ob'ject. uid; II Rounded lines, thickness of zero. ObjectCall(msgDcSetLineThickness, data.dc, (P_ARGS)O); i f (DbgFlagGet (' F', Ox40L» ( Debugf("Use a non-zero line thickness."); ObjectCall(msgDcSetLineThickness, data. dc, (P_ARGS)2); } II II Open a font. Use the "user input" font (whatever the user has chosen for this in System Preferences. fs.id 0; fs.attr.group sysDcGroupUserInput; fs.attr.weight sysDcWeightNormal; fs.attr.aspect sysDcAspectNormal; fs.attr.italic 0; fs.attr.monospaced 0; fs.attr.encoding sysDcEncodeGoSystem; ObjCaIIJmp(msgDcOpenFont, data,dc, &fs, s, Error); II II II Scale the font. The entire DC will be scaled in the repaint to pleasingly fill the window. fontScale.x = fontScale.y = FxMakeFixed(initFontScale,O); ObjectCall(msgDcScaleFont, data.dc, &fontScale); II Bind the window to the dc. ObjectCall(msgDcSetWindow, data.dc, (P_ARGS)self); II Update the instance data. ObjectWrite(self, ctx, &data); return stsOK; MsgHandlerParametersNoWarning; II suppress compiler warnings about unused parameters Error: ObjCaIIWarn(msgDestroy, data.dc, Nil(OBJ_KEY»; return s; 1* HelloWinInit *1 Create a new window object. HELLO WORLD (CUSTOM WINDOW) I C:AMDI ~ rnn~ .... -0 .. o 1***************************************************** *********************** HelloWinFree Free self. ****************************************************** **********************1 MsgHandlerWithTypes(HelloWinFree, P_ARGS, P_INSTANCE_DATA) { II Destroy the dc. (Assumes that this will not fail.) II Note that pData is now invalid. ObjCallWarn (msgDestroy, pData->dc, Nil (P_ARGS»; I I Ancestor will eventually free self. returnstsOK; MsgHandlerParametersNoWarning; } 1* HelloWinFree *1 I*******************************~********************* **********~************ HelloWinRepaint Repaint the window .. This is the only paint. routine·needed; clsHelloWin relies on the window system to tell it when it needs (re)pa¥nting. ***********************************.******.*******~*** ***:*********************1 MllgHandlerWithTypes(HelloWinRepaint, P_ARGS, P_INSTANCE_DATA) { SYSDC~TEXT_OUTPUT S32 S32 SYSDC FONT·METRICS SIZE32 WIN METRICS FIXED SCALE. RECT32 XY32 STATUS II II tx; textWidth; helloAdjust, worldAdjust; fm; drawingSize; . wm; drawinqAspect, winAspect; scale; dotRect; bezier[4}; s; Determine size of drawing in 100 units to a point coord. system. II The words "Hello" arid "World" have no descenders (in most fonts!!). Height is font height (initFontScale) * 2 - the descender size. II Width is max of the two text widths plus em.width (width of II the exclamation point. II II II II Figure out the widths of the two text strings. Init tx. memset('tx, 0, sizeof(SYSDC TEXT OUTPUT»; tx.underline = 0; -tx.alignChr = sysDcAlignChrBaseline; tx.stop =.maxS32; tx.spaceChar = 32; II Set the overall text width to whichever text string is wider. tx.cp.x =·0; tx.cp.y = 0; tx.pText = "World"; tx.lenText = strlen(tx.pText); ObjectCall(msgDcMeasureText, pData->dc, ,tx); textWidth = tx·.cp.x; tx.cp.x 0; tx.cp.y 0; tx.pText "Hello"; tx.lenText strlen(tx.pText); ObjectCall(msgDcMeasureText,pData->dc, ,tx); if (tx.cp.x > textWidth) { II "Hello" is wider helloAdjust = 0; worldAdjust = (tx.cp.x - textWidth) I 2; textWidth = tx.cp.x; else { II "World" was wider worldAdjust = 0; helloAdjust = (textWidth - tx.cp.x) / 2; II Get font metrics. ObjectCall(msgDcGetFontMetrics, pData->dc, 'fm); drawingSize.w = textWidth + fm.em.w; II Remember, descenderPos is negative. drawingSize.h ~ (2 * initFontScale) + fm.descenderPos; II II Must bracket all repainting with msgWinBegin/EndRepaint. II II The window system figures out which part of the window needs repainting, and restricts all painting operations t9 that update II area. II ObjCallRet(msgWinBeginRepaint, pData->dc, pNull, s); /1 Fill the background with white to start. ObjectCall(msgDcFiIIWindow, pData->dc, pNull); II II II II II II We have determined the size of the drawing in points. But if the window is much smaller than this the drawing will be cropped. So, we must scale it to fit the window. You can scale a DC to match the width and height of a window using dcUnitsWorld, but then the text would be stretched strangely. II II II Instead, we'll compute a consistent scaling factor for the drawing. II II We II We II . need to first determine the size of the window. send the message to the DC to get the size in DC units. ObjCaIIJmp(msgWinGetMetrics, pData->dc, 'wm, s, exit); ~ II II bezier[O].x textWidth + (fm.em.w I 2); bezier[O] .y fm.ascenderPos; bezier[l] .x textWidth; bezier[l] .y = initFontScale * 3 I 2; bezier[2] .x = bezier[l] .x; bezier[2] .y = initFontScale + fm.ascenderPos; bezier[3].x =bezier[O] .x; bezier[3].y = bezier[2] .y; ObjectCall(msgDcDrawBezier, pData->dc, bezier); Now decide whether to scale by the x or y coordinate. Have to hassle with Fixed Point! drawingAspect = FxDivIntsSC(drawingSize.h, drawingSize.w); winAspect = FxDivIntsSC(wm.bounds.size.h, wm.bounds.size.w); if (winAspect > drawingAspect ) { /! II The window is "taller" than the drawing. Scale so the II drawing fills the window horizontally. /! Dbg (Debugf ("Window is taller than drawing I Still must calculate vertical offset!");) scale.x = scale.y = FxDivIntsSC(wm.bounds.size.w, drawingSize.w); else /! II The window is "wider" than the drawing. Scale so the II drawing fills the window vertically. /! Dbg (Debugf ("Window is wider than drawing! Still must calculate horizontal offset! ") ;) scale.x = scale.y = FxDivIntsSC(wm.bounds.size.h, drawingSize.h); II Then the right half ... bezier[l] .x = textWidth + fm.em.w; bezier[2].x = bezier[l] .x; ObjectCall(msgDcDrawBezier, pData->dc, bezier); II Paint the dot. dotRect.origin.x = textWidth + (fm.em.w - fm.xPos) dotRect.origin.y = lineThickness I 2; dotRect.size.w = dotRect.size.h = fm.xPos; ObjectCall(msgDcDrawEllipse, pData->dc, &dotRect); II Fall through to return. s = stsOK; ObjectCall(msgDcScale, pData->dc, &scale); exit: /! II II II I 2; At this point a more sophisticated program would figure out which parts need redrawing based on the boundaries of the dirty area. 1/ II Display the text. I I Display "Hello". tx was set to do this from before, but need to II reset tx.lenText because msgDcMeasureText passes back in it the II offset of the last character that would be drawn in it. tx.cp.x helloAdjust; tx.cp.y = initFontScale; tx.lenText = strlen(tx.pText); ObjectCall(msgDcDrawText, pData->dc, &tx); II Display "World". tx.cp.x worldAdjust; tx.cp.y 0; tx.pText "World"; tx.lenText strlen(tx.pText); ObjectCall(msgDcDrawText, pData->dc, &tx); II Paint the exclamation point. ObjectCall(msgDcSetForegroundRGB, pData->dc, (P_ARGS)sysDcRGBGray66); II Want Foreground color of Gray for edges of Exclamation Point. ObjectCall(msgDcSetBackgroundRGB, pData->dc, (P_ARGS)sySDCRGBGray33); ObjectCall(msgDcSetLineThickness, pData->dc, (P_ARGS)lineThickness); II Paint the teardrop. II First the left half ... ObjCallWarn(msgWinEndRepaint, self, Nil(P_ARGS)); II Need to restore state if no errors, so might as well do it always. ObjectCall(msgDcSetForegroundRGB, pData->dc, (P_ARGS)sysDcRGBBlack); ObjectCall(msgDcSetBackgroundRGB, pData->dc, (P_ARGS)sysDcRGBWhite); ObjectCall(msgDcSetLineThickness, pData->dc, (P_ARGS)O); i f (DbgFlagGet('F', Ox40)) ( Dbg(Debugf("Use a non-zero line thickness.");) ObjectCall(msgDcSetLineThickness, .pData->dc, (P_ARGS) 2) ; return s; MsgHandlerParametersNoWarning; } 1* HelloWinRepaint *1 1***************************************************** *********************** ClsHelloWinInit Install the class. ****************************************************************************1 STATUS ClsHelloWinInit (void) ( CLASS NEW new; STATUS S; II Create the class. ObjCallWarn(msgNewDefaults, clsClass, &new); new.object.uid clsHelloWin; new.cls.pMsg = clsHelloWinTable; ~ HELLO WORLD (CUSTOM WINDOW) I SAMPLE CODE 00 w .... 00 new.cls.ancestor clsWin; new. cIs. size SizeOf (INSTANCE_DATA) ; new.cls.newArgsSize SizeOf(HELLO_WIN_NEW); ObjCaIIRet(msgNew, clsClass, &new, s); if (DbgFlagGet('F', Ox20» ( Debugf("Turning on message tracing for clsHelloWin"); (void)ObjCaIIWarn(msgTrace, clsHelloWin, (P_ARGS) true); return stsOK; } /* ClsHelloWinlnit */ DLLlNIT.C /**************************************************************************** File; dllinit.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision; 1.3 $ $Date; 13 Nov 1991 18;05;02 $ This file contains the initialization routine for the Hello World dll. ****************************************************************************/ iinclude iinclude // The creation routines for each class in this dll. STATUS ClsHelloWinInit (void); /**************************************************************************** DLLMain Initialize DLL ****************************************************************************/ STATUS EXPORTED DLLMain (void) ( STATUS s; Dbg{Debugf("Beginning hello.dll initialization.");) StsRet(ClsHelloWinlnit(), s); Dbg (Debugf ("Completed hello. dll initialization");) return stsOK; } /* DLLMain '*/ DLL.LBC ++DLLMAIN.'GO-HELLO_DLL-Vl(O)' MAKEFILE iiiiiiiiii#iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii i i i i i i i i i i i i i i WMake Makefile for Hello World Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. i i $Revision; i $Date; 1.5 $ 13 Nov 1991 18;05;08 $ i iiiiiiiiiiiiiiiiUiiiiiiiiiiiiiiiiiiiiiiiiiiiii i Set PENPOINT PATH to your environment variable, if it exists. i Otherwise, s~t it to \penpoint !ifdef %PENPOINT PATH PENPOINT_PATH = $ (%PENPOINT_PATH) !else PENPOINT PATH = \penpoint !endif i The DOS name of your project directory PROJ = hello i Standard defines for sample code !INCLUDE $(PENPOINT_PATH)\sdk\sample\sdefines.mif i The PenPoint name of your application EXE NAME = Hello World i The linker name for your executable ; company-name-V «minor» EXE_LNAME= GO-HELLO_EXE-Vl (0) i Object files needed to build your app EXE_OBJS = heltbl.obj hello.obj i Libs needed to build your app EXE_LIBS $ (DLL_NAME) penpoint app i The linker name for the optional DLL company-name-V «minor» DLL_LNAME= GO-HELLO_DLL-Vl{O) i Files for the optional DLL DL1 OBJS = helwtbl.obj dllinit.obj hellowin.obj * Counter Applicalilion Libs needed to build the optional DLL DLL_LIBS = penpoint win Targets all: $ (APP_DIR) \$ (PROJ) .exe $ (APP_DIR)\$ (PROJ) .dll .SYMBOLIC The clean rule must be :: because it is also defined ih srules clean:: .SYMBOLIC -del heltbl.h -del heltbl. tc -del helwtbl.h -del helwtbl.tc -del hello . lib Dependencies hello.obj: hello.c heltbl.h hellowin.h hellowin.obj: hellowin.c heltbl.h hellowin.h Standard rules for sample code !INCLUDE $ (PENPOINT_PATH)\sdk\sample\srules.mif * Counter Application displays a number in on the screen. Every time you turn to its page, Counter Application increments the number. It also lets you choose the format in which to display the number (decimal, octal, or hexadecimal) . * * * HELLO.DLe GO-HELLO_DLL-Vl(O) GO-HELLO_EXE-Vl(O) hello.dll hello.exe Obiectives Counter Application is used in the ADC labs. This sample application also shows how to: • Save and restore application state • Memorymap state data. COUNTER APPLICATION I SAMPLE CODE :0VI .. 00 0\ Class Overview CNTRAPP.C clsCntrApp's code and initialization Counter Application defines two classes: clsCntr and clsCntrApp. It makes use of the following classes: CNTRAPP.H header file for clsCntrApp clsApp cIsAppMgr clsClass clsFileHandle clsMenu clsMenuButton clsObject cIsLabel ·Co.plling To compile Counter Application, just cd \penpoint\sdk\sample\cntrapp wmake This compiles the application and creates CNTRAPP.EXE in \PENPOINT\APP\CNTRAPP. Running After compiling Counter Application, you can run it by 1 Adding \\boot\penpoint\app\CounterApplication to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Creating a new Counter Application document, and turning to it. . Alternatively, you can boot PenPoint and then install Counter Application through the Connections Notebook. Files Used The code for Counter Application is in \PENPOINT\SDK\SAMPLE\CNTRAPP. The files are: CNTR.C clsCntr's code and initialization CNTR.H header file for cIsCntr METHODS.TBL method tables for Counter Application. METHODS.TBl /**************************************************************************** File: methods.tbl Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 07 Jan 1992 16:32:54 $ $Date: This file contains the method tables for the classes in CntrApp. ****************************************************************************/ 'ifndef CLSMGR_INCLUDED 'include 'endif 'ifndef APP_INCLUDED 'include 'endif 'ifndef CNTR_INCLUDED 'include 'endif MSG_INFO clsCntrMethods[l msgNewDefaults, msgInit, msgSave, msgRestore, msgFree, msgCntrGetValue, msgCntrIncr, "CntrNewDefaults" , "CntrInit", "CntrSave" , "CntrRestore" , "CntrFree" , "CntrGetValue", "CntrIncr" , objCallAncestorBefore, objCallAncestorBefore, objCallAncestorBefore, objCallAncestorBefore, objCallAncestorAfter, 0, 0, }; MSG_INFO clsCntrAppMethodsJl msgInit, msgSave, msgRestore, "CntrAppInit", "CntrAppSave", "CntrAppRestore", objCallAncestorBefore, objCallAncestorBefore, objCallAncestorBefore, o msgFree, msgApplnit, msgAppOpen, msgAppClose, msgCntrAppChangeFormat, "CntrAppFree" , "CntrAppApplnit" , "CntrAppOpen" , "CntrAppClose" , "CntrAppChangeFormat", objCallAncestorAfter, objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, 0, o }; CLASS_INFO classlnfol] = { "clsCntrTable", clsCntrMethods, 0, "clsCntrAppTable", clsCntrAppMethods, 0, o }; S32 initia1Value; CNTR_NEW_ONLY, *P_CNTR_NEW_ONLY; #define cntrNewFields \ objectNewFields \ CNTR_NEW_ONLY cntr; typedef struct CNTR_NEW { cntrNewFields ) CNTR_NEW, *P_CNTR_NEW; 1***************************************************** *********************** msgCntrlncr takes void, returns STATUS Bumps counter value by one. ****************************************************************************/ #define msgCntrIncr MakeMsg(clsCntr, 1) /**************************************************************************** CNTR.H 1***************************************************** *********************** File: cntr.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice .(including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ $Date: 1.3 Nov 1991 17:55:24 $ This file contains the API definition for clsCntr. ****************************************************** **********************1 #ifndef CNTR INCLUDED #define CNTR_INCLUDED #ifndef CLSMGR_INCLUDED #include #endif #define clsCntr MakeWKN( 1, 1, wknPrivate) #define stsCntrMaxReached MakeStatus(clsCntr, 1) STATUS GLOBAL ClsCntrlnit (void); msgCntrGetValue takes P_CNTR_INFO, returns STATUS Passes back. counter value. ****************************************************** **********************1 #define msgCntrGetValue MakeMsg(clsCntr, 2) typedef struct CNTR_INFO { S32 value; } CNTR_INFO, *P_CNTR_INFO; #endif II CNTR_INCLUDED CNTR.C /**********************~****************************** *********************** File: cntr.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ 07 Jan 1992 16:31:56 $ $Date: This file contains the class definition and methods for clsCntr. ****************************************************************************/ I~**************************************************** *********************** msgNew takes P_CNTR_NEW, returns STATUS Creates a new counter object. ****************************************************** **********************1 typedef struct CNTR NEW ONLY #ifndef DEBUG_INCLUDED #include #endif ·#ifndef FS_INCLUDED #include #endif COUNTER APPLICATION I "AMPI F rnnF ..... 00 ~ o tifndef CNTR INCLUDED tinclude fendif finclude 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 typedef struct CNTR_INST S32 currentValue; CNTR_INST, *P_CNTR_INST; 1* * * * * * * * * * * * * * * * ~ * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** 1***************************************************** *********************** • CntrSave Respond to msgSave. ****************************************************** **********************1 MsgHandlerArgType(CntrSave, P_OBJ_SAVE) { STREAM_READ_WRlTE fsWrite; STATUS s; Debugf("Cntr:CntrSave"); 1/ II Write instance to the file. 1/ fsWrite.nurnBytes= SizeOf(CNTR_INST); fsWrite.pBuf= pData; ObjCallRet(msgStrearnWrite, pArgs->file, &fsWrite, s); return stsOK; MsgHandlerPararnetersNoWarning; } 1* CntrSave *1 ~ntrNewDefaults Respond to msgNewDefaults. ****************************************************** **********************1 MsgHandlerArgType(CntrNewDefaults, P_CNTR_NEW) 1***************************************************** *********************** CntrRestore Respond to msgRestore. { *********************************************~******** **********************1 Debugf("Cntr:CntrNewDefaults"); II Set default value in new struct. pArgs->cntr.initialValue '= 0; returnstsOK; MsgHandlerPararnetersNoWarning; } 1* CntrNewDefaults *1 MsgHandlerArgType(CntrRestore, P_OBJ_RESTORE) { CNTR_INST inst; STREAM_READ_WRITE fsRead; STATUS s; Debugf("Cntr:CntrRestore") ; 1/ II Read instance data from the file. II fsRead.nurnBytes= SizeOf(CNTR INST); fsRead.pBuf= &inst; , ObjCallRet(msgStrearnRead, pArgs->file, &fsRead, s); 1/ II Update instance data. 1/ ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerPararnetersNoWarning; } 1* CntrRestore *1 1***************************************************** *********************** Cntrlnit Respond to msglnit. ****************************************************** *****************~****I MsgHandlerArgType(Cntrlnit, P CNTR NEW) { . - - CNTR_INST inst; Debugf ("Cntr:CntiInit"); II Set starting value. inst.currentValue = pArgs->cntr.initialValue; II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerPararnetersNoWarning; } 1* Cntrlnit *1 1***************************************************** *********************** CntrFree Respond to msgFree. ****************************************************** **********************1 MsgHandler(CntrFree) { Debugf("Cntr:CntrFree"); return stsOK; MsgHandlerParametersNoWarning; } 1* CntrFree *1 /**************************************************************************** CntrGetValue Respond to msgCntrGetValue. ****************************************************************************/ MsgHandlerWithTypes(CntrGetValue, P_CNTR_INFO, P_CNTR_INST) CNTRAPP.H { Debugf ("Cntr: CntrGetValue") ; pArgs->value = pData->currentValue; return stsOK; MsgHandlerParametersNoWarning; } 1* CntrGetValue *1 1**************************************************************************** 1**************************************************************************** Cntrlncr Respond to msgCntrlncr. ****************************************************************************1 MsgHandler(Cntrlncr) { CNTR_INST inst; Debugf("Cntr:Cntrlncr"); inst = IDataDeref(pData, CNTR_INST); inst.currentValue++; ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; } 1* Cntrlncr *1 Installation File: cntrapp. h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 13 Nov 1991 17:55:26 $ $Date: This file contains definitions for clsCntrApp. ****************************************************************************1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * new.object.uid = clsCntr; new.cls.pMsg clsCntrTable; new.cls.ancestor clsObject; new.cls.size SizeOf(CNTR_INST); new.cls.newArgsSize SizeOf(CNTR_NEW); ObjCallJmp(msgNew, clsClass, &new, s, Error); return stsOK; Error: return s; } 1* ClsCntrlnit *1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** ClsCntrlnit Create the class. *ifndef CNTRAPP INCLUDED *define CNTRAPP_INCLUDED *ifndef CLSMGR_INCLUDED *include *endif II Define a well known UID for the app *define clsCntrApp MakeWKN(555, 1, wknGlobal) II Define a message *define msgCntrAppChangeFormat MakeMsg(clsCntrApp,l) *endif II CNTRAPP_INCLUDED ****************************************************************************1 CNTRAPP.C STATUS GLOBAL ClsCntrlnit (void) 1**************************************************************************** ( CLASS_NEW new; STATUS s; ObjCallJmp(msgNewDefaults, clsClass, &new, s, Error); File: cntrapp.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE COUNTER APPUCATION I <:'AMIII ~ rnn~ ...00 00 loa IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WAruRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WIL:L GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS S~1PLE CODE. $Revision: 1.2 $ 07 Jan 1992 16: 32: 02 !1 $Date: This file contains the implementation of the counter application class. ****************************************************************************/ fifndef APP_INCLUDED finclude fendif fifndef APPMGR_INCLUDED tinclude tendif tifndef OS INCLUDED #include fendif tifndef RESFILE_INCLUDED finclude fendif fifndef FRAME_INCLUDED finclude fendif fifndef DEBUG_INCLUDED finclude fendif lifndef TKTABLE_INCLUDED tinc1ude fendif fifndef MENU~INCLUDED linclude fendif fifndef CNTR_INCLUDED linclude lendif fifndef CNTRAPP_INCLUDED #include fendif finclude finc1ude finc1ude /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef enurn CNTRAPP DISPLAY FORMAT dec, oct, hex CNTRAPP_DISPLAY_FORMAT, *P_CNTRAPP_DISPLAY_FORMAT; typedef struct CNTRAPP_INST ( P CNTRAPP DISPLAY FORMAT pFormat; OBJECT fileHandle; counter; OBJECT CNTRAPP_INST, *P_CNTRAPP_INST; static TK_TABLE_ENTRY CntrAppMenuBar[] = ( ("Representation", 0, 0, 0, tkMenuPullDown, clsMenuButton), ("Dec", msgCntrAppChangeForrnat, dec), ("Oct", msgCntrAppChangeForrnat, oct), ("Hex", msgCntrAppChangeForrnat, hex), (pNull) , (pNull) }; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**************************************************************************** BuildString Local function to build a label string ****************************************************************************/ STATUS LOCAL BuildString( P_STRING p, P_CNTRAPP_INST pData) CNTR_INFO ci; STATUS s; ObjCallRet(msgCntrGetValue, pData->counter, switch (*(pData->pForrnat)) ( case dec: sprintf(p, " Counter value is %d ", break; case oct: sprintf(p, " Counter value is %0 ", break; case hex: sprintf (p, " Counter value is %x ", break; default: sprintf(p, " Unknown Representation break; return stsOK; . } /* BuildString */ . &ci ,s); ci.va1ue); ci.value); ci. value) ; Requested "); o o 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 I*******************~********************************* *********************** CntrApplnit Respond to msglnit. ***************************~************************** **********************1 MsgHandler(CntrApplnit) ( CNTRAPP_INST inst; Debugf("CntrApp:CntrApplnit"); inst.counter = pNull; inst.fileHandle = pNull; inst.pFormat = pNull; II Update instance data. ObjectWrite(self, ct~, &inst); return stsOK; MsgHandlerParametersNoWarning; } 1* CntrApplnit *1 1***************************************************** *********************** ~CntrAppSave Respond to msgSave. Save the counter object using "ResPutObject. The counter will receive msgSave and save any data it needs. The application doesn't need to save any data, the format data is memory mapped. ****************************************************** **********************1 MsgHandlerWithTypes(CntrAppSave, P_OBJ_SAVE, P_CNTRAPP_INST) { STATUS s; Debugf ("CntrApp: CntrAppSave") ; II Save the counter object. ObjCallRet(msgResPutObject, pArgs->file, pData->counter, s); return stsOK; MsgHandlerParametersNoWarning; } 1* CntrAppSave *1 1***************************************************** *********************** CntrAppRestore Respond to msgRestore. Open the file holding the application data, memory map the file. Restore the counter object by sending msgResGetObject -- the counter will receive msgRestore. FS NEW fsn; CNTRAPP INST inst; STATUS s; Debugf("CntrApp:CntrAppRestore"); II Get handle for format file, save the handle ObjCallRet(msgNewDefaults, clsFileHandle, &fsn, s); fsn.fs.locator.pPath = "formatfile"; fsn.fs.locator.uid = theWorkingDir; ObjCallRet(msgNew, clsFileHandle, &fsn, s); inst.fileHandle = fsn.object.uid; II Map the file to memory ObjCallRet(msgFSMemoryMap, fsn.object.uid, &inst.pFormat, s ); II Restore the counter object. ObjCallJmp{msgResGetObject, pArgs->file, &inst.counter, s, Error); II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerparametersNoWarning; Error: return s; } 1* CntrAppRestore *1 1***************************************************** *********************** CntrAppFree Respond to msgFree. ***********************************************~****** **********************1 MsgHandlerWithTypes(CntrAppFree, P_ARGS, P_CNTRAPP_INST) ( STATUS s; Debugf("CntrApp:CntrAppFree"); ObjCallRet(msgDestroy, pData->counter, Nil(P~GS), s); II Unmap the file ObjCallRet(msgFSMemoryMapFree, pData->fileHandle, Nil(P_ARGS), s); II Free the file handle ObjCallRet(msgDestroy, pData->fileHandle, Nil(P_ARGS), s ); return stsOK; MsgHandlerParametersNoWarning; } 1* CntrAppFree *1 1***************************************************** *********************** Cnt rAppApp Init Respond to msgApplnit. Create the file to hold the memory mapped data. ****************************************************** **********************1 ****************************************************** **********************1 MsgHandlerWithTypes(CntrAppRestore, P_OBJ_RESTORE, P_CNTRAPP_INST) ( MsgHandler(CntrAppApplnit) { COUNTER APPUCATION I "AMPI F rnDF ...o .. cn; CNTR NEW fsn; FS NEW STREAM READ WRITE fsWrite; CNTRAPP DISPLAY FORMAT format; CNTRAPP INST inst; STATUS s; Debugf("CntrApp:CntrAppAppInit"l; inst = IDataDeref(pData, CNTRAPP_INST); 1/ II Create the counter object. II ObjCallRet(msgNewDefaults, clsCntr, 'cn, sl; cn.cntr.initiaIValue = 42; ObjCaIIRet(msgNew, clsCntr, &cn, s); inst.counter = cn.object.uid; II II Create a file, fill it with a default value 1/ ObjCallRet(msgNewDefaults, clsFileHandle, &fsn, s); fsn.fs.locator.pPath = "formatfile"; fsn.fs.locator.uid = theWorkingDir; ObjCaIIRet(msgNew, clsFileHandle, &fsn, s); format = dec; fsWrite.numBytes = SizeOf(CNTRAPP_DISPLAY_FORMAT); fsWrite.pBuf = &format; ObjCaIIRet(msgStreamWrite, fsn.object.uid, &fsWrite, s); inst.fileHandle = fsn.objec~.uid; II II II Map the file to memory ObjCaIIRet(msgFSMemoryMap, fsn.object.uid, &inst.pFormat, s); II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; 1* CntrAppAppInit*1 1***************************************************** *********************** CntrAppOpen Respond to msgAppOpen. It's important that the ancestor be called AFTER all the frame manipulations in this routine because the ancestor takes care of any layout that is necessary. ****************************************************** **********************1 MsgHandlerWithTypes(CntrAppOpen, P_ARGS, P_CNTRAPP_INST) ( APP_METRICS am; MENU NEW mn; LABEL NEW In; STATUS s; char buf[30]; Debugf("CntrApp:CntrAppOpen"); II Increment the counter. -ObjCaIIRet(msgCntrIncr, pData->counter, Nil(P_ARGS), s); II Build the string for the label. StsRet(BuildString(buf, pData), s); II Create the label. ObjCaIIRet(msgNewDefaults, clsLabel, &In, s); In.label.pString = buf; In.label.style.scaleUnits = bsUnitsFitWindowProper; In.label.style.xAlignment = IsAlignCenter; In.label.style.yAlignment = IsAlignCenter; ObjCaIIRet(msgNew, clsLabel, &In, s); II Get app metrics. ObjCaIIJmp(msgAppGetMetrics, self, &am, s, Error); II Set the label as the clientWin. ObjCallJmp(msgFrameSetClientWin, am.mainWin, In.object.uid, s, Error); II Create and add menu bar. ObjCaIIJmp(msgNewDefaults, clsMenu, &mn, s, Error); mn.tkTable.client = self; mn.tkTable.pEntries = CntrAppMenuBar; ObjCaIIJmp(msgNew, clsMenu, &mn, s, Error); ObjCaIIJmp(msgAppCreateMenuBar, self, &mn.object.uid, s, Error); ObjCallJmp(msgFrameSetMenuBar, am.mainWin, mn.object.uid, s, Error); return stsOK; MsgHandlerParametersNoWarning; Error: return s; } 1* CntrAppOpen *1 1***************************************************** *********************** CntrAppClose Respond to msgAppClose. Be sure that the ancestor is called FIRST. The ancestor extracts the frame, and we want the frame extracted before performing surgery on it. ****************************************************** **********************1 MsgHandler(CntrAppClose) ( APP_METRICS am; STATUS s; Debugf ("CntrApp:CntrAppClose"); II Free the menu bar. ObjCaIIJmp(msgAppGetMetrics, self, &am, s, Error); ObjCaIIJmp(msgFrameDestroyMenuBar, am.mainWin, pNull, s, Error); return stsOK; ...o ... MsgHandlerParametersNoWarning; Error: return s; ) 1* CntrAppClose *1 1***************************************************** *********************** CntrAppChangeFormat Respond tomsgCntrAppChangeFormat. Update the memory mapped data. ****************************************************** **********************1 MsgHandlerWithTypes (CntrAppChangeFormat', P_ARGS, P_CNTRAPP_ INST) ( APP METRICS am; WIN thelabel; STATUS s; buf[30]; char Debugf("CntrApp:CntrAppChangeFormat"); II II Update mmap data II *(pData->pFormat) = (CNTRAPP_DISPLAY_FORMAT) (U32)pArgs; II Build the string for the label. StsRet(BuildString(buf, pData), s); II Get app metrics. ObjCallRet(msgAppGetMetrics, self, &am, a); II Get the clientWin. ObjCallRet(msgFrameGetClientWin, am.mainWin, &thelabel, s); II Set the label string. ObjCallRet(msgLabelSetString, thelabel, buf, s); return stsOK; MsgHandlerParametersNoWarning; ) 1* CntrAppChangeFormat *1 new.object.uid clsCntrApp; new.cls.pMsg clsCntrAppTable; new. cls. ancestor clsApp; new.cls.size SizeOf(CNTRAPP_INST); new.cls.newArgsSize SizeOf(APP_NEW); strcpy(new.apPMgr.company, "GO Corporation"); strcpy(new.appMgr.defaultDocName, "Counter Application"); ObjCallJmp(msgNew, clsAppMgr, &new, s, Error); return stsOK; Error: return S; ) 1* ClsCntrAppInit *1 1***************************************************** *********************** main Main application entry point. ****************************************************** **********************1 void CDECL main ( int argc, argv[], char * U16 processCount) if (processCount == 0) ( StaWarn(ClsCntrAppInit(»; AppMonitorMain(clsCntrApp, objNull); else ( StsWarn(ClsCntrInit(»; AppMain(); Unused(argc); Unused(argv); warnings ) 1* main *1 II Suppress compiler's "unused parameter" 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Inf!tallation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 '* 1***************************************************** *********************** ClaCntrAppInit Create the application class. ****************************************************** **********************1 STATUS GLOBAL ClsCntrAppInit (void) ( APP_MGR_NEW new; STATUS s; ObjCallJmp(msgNewDefaults, clsAppMgr, &new, s, Error); I SAMPLE CODE MAKE FILE iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii i i i i i i i i i i i i WMake Makefile for CounterApp Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU COUNTER APPUCATION loa o Col . ... Tac-Tac-Toe otoI· # FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF # THE USE OR INABILITY TO USE THIS SAMPLE CODE. # # $Revision: 1.4 $ # $Date: 07 Jan 1992 16:31:50 $ # #######11###################################### # Set PENPOINT_PATH to your environment variable, if it exists. # Otherwise, set it to \penpoint !ifdef %PENPOINT- PATH , PENPOINT_PATH= $(%PENPOINT_PATH) !else PENPOINT PATH = \penpoint !endif # The DOS name of your project directory. PROJ = cntrapp # Standard defines for sample code (needs the PROJ) definition !INCLUDE $(PENPOINT_PATH)\sdk\sample\sdefines.mif # The PenPoint name of your application EXE_NAME = Counter Application # The linker name for your executable : company-name-V «minor» EXE_ LNAME = GO-CNTRAPP-V1 (0) # Object files needed to build your app EXE_OBJS = methods.obj cntr.obj cntrapp.obj # Libs needed to build your app EXE_LIBS = penpoint app # Targets all: $ (APP_DIR) \$ (PROJ) .exe .SYMBOLIC # The clean rule must be :: because it is also defined insrules clean: : -del methods.h -del methods.tc # Dependencies cntrapp.obj: cntrapp.c methods.h cntrapp.h cntr.h cntr.obj: cntr.c methods.h cntr.h' methods.obj: methods.tb1 cntr.h cntrapp.h # Standard rules for sample code ! INCLUDE $ (PENPOINT_PATH) \sdk\samp1e\srules'.mif Tic-T ac-T oe presents a tictactoe board and lets the user enter Xs and Os on it. It is not a true computerized game-the user does not play tic-tac-toe against the computer. Instead, it assumes that that there are two users who want to play the game against each other. Although a tic-tac-toe game is not exactly a typical notebook application, TicTacToe has many of the characteristics of a fullblown PenPoint application~ It has a graphical interface, handwritten input, keyboard input, gesture support, use of the notebook metaphor, versioning of filed data, selection, import/export, option cards, undo support, stationery, help text, and so on. Obiectives clsNote This sample application shows how to: clsObject clsOptionTable • Store data in a separate data object • Display data in a view • Accept handwritten and keyboard input • Implement gesture handling clsView • Support most of the standard application menus (e.g., move, copy, delete, and undo) clsXferList • Add applicationspecific menus clsXText • Add applicationspecific option cards Compiling • Provide help To compile TicTacToe, just • Provide quick help (using tags in the resource list) for the view, an option card, and the controls in the option card cd \penpoint\sdk\sample\ttt wmake • Provide stationery documents This compiles the application and creates TTT.EXE and APP.RES in \PENPOINT\APP\TTT. Next, you need to make the stationery and help: • Have both large and small applicationspecific document icons wmake stationery wmake help • Provide customized undo strings • Use ClsSymbolsInitO • SpecifY an application version number. clsPen clsScrollWin clsSysDrwCtx clsXGesture These two steps make \STATIONRY and \HELP directories in \PENPOINT\APP\TTT, and copy the stationery and help files there. Running Class Overview After compiling TicTacToe, you can run it by TicTacToe defines three classes: clsTttApp, clsTttView, and clsTttData. It makes use of the following classes: 1 Adding \ \boot\penpoint\app \TicT acT oe to \PENPO INT\BOOT\APP .INI 2 Booting PenPoint 3 Creating a new TicTacToe document, and turning to it. clsApp clsAppMgr Alternatively, you can boot PenPoint and then install TicTacToe through the Connections Notebook. clsClass clsFileHandle clsIntegerField Files Used clsIP The code for TicTacToe is in \PENPOINT\SDK\SAMPLE\TTT. The files are: clsKey FILLED.TXT stationery file (filled with Xs and Os) clsMenu LGICON.RES large document icon resource file (compiled) I TlC-TAC-TOE SAMPLE CODE ~ o III lot o METHODS.TBL the method tables for all of the TicTacToe classes RULES. TXT help file (containing the rules for the game) S3TT.C symbol name definitions and call to ClsSymbolslnitO SMICON.RES small document icon resource file (compiled) . STRAT.TXT help file (containing a strategy for playing the game) TTTAPP.C clsTttApp's code and initialization TTTAPP.H header file for the application class TTTDATA.C clsTttData's code and initialization TTTDATA.H header file for the data class TTTDBG.C debuggingrelated message handlers TTTIPAD.C insertion padrelated message handlers TTTMBAR.C menu barrelated message handlers TTTPRIV.H private include file for TicTacToe TTTQHELP.RC quick help resource file (uncompiled) TTTUTIL.C utility functions TTIVIEW.cclsTttView's code and initialization TTIVIEW.H header file for the view class TTIVOPT.C clsTttView's option cardrelated message handlers TITVXFER.C clsTttView's data transferrelated message handlers XSONLY.TXT stationery file (partially filled, with Xs only). METHODS.TBl /**************************************************************************** File: methods.tbl Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way' you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE .LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.0 $ 07 Jan 1992 17:19:06 $ $Date: This file contains the method tables for the classes in TttApp. ****************************************************************************/ " ,ifndef CLSMGR_INCLUDED 'include 'endif 'ifndef TTTPRIV_INCLUDED tinclude 'endif tifndef TTTDATA_INCLUDED 'include 'endif tifndef TTTVIEW_INCLUDED 'include tendif tifndef TTTAPP_INCLUDED tinclude 'endif ,ifndef WIN_INCLUDED tinclude 'endif ,ifndef APP_INCLUDED 'include tendif iifndef SEL_INCLUDED Unclude iendif iifndef GWIN_INCLUDED 'include tendif ,ifndef OPTION_INCLUDED iinclude 'endif iifndef UNDO_INCLUDED 'include iendif tifndef XFER_INCLUDED tinclude .endif MSG_INFO clsTttViewMethods[] msgNewDefaults, msgInit, . msgFree, msgSave, msgRestore, Hfdef DEBUG msgDurnp, 'endif msgTttDataChanged, msgWinRepaint, = ( "TttViewNewDefaults", "Tt tViewIni t " , "TttViewFree", "TttViewSave" , "TttViewRestore", objCallAncestorBefore, "TttViewDurnp" , objCallAncestorBefore, 0, objCallAncestorAfter, objCallAncestorBefore, objCallAncestorBefore, "TttViewDataChanged", "TttViewRepaint ", 0, 0, msgWinGetDesiredSize, msgGWinGesture, msgTttViewGetMetrics, msgTttViewSetMetrics, msgTttViewToggleSel, msgTttViewTakeSel, msglnputEvent, msgXferGet, msgXferList, msgOptionApplyCard, msgOptionRefreshCard, msgOptionProvideCardWin, msgOptionAddCards, msgOptionApplicableCard, msgSelYield, msgSelBeginMove, msgSelBeginCopy, msgSelMoveSelection, msgSelCopySelection, msgSelDelete,. msgControlProvideEnable, o "TttViewGetDesiredSize", "TttViewGesture", "TttViewGetMetrics", "TttViewSetMetrics", "TttViewToggiesel", "TttViewTakeSel", "TttViewlnputEvent", "TttViewXferGet", "TttViewXferList", "TttViewOptionApplyCard", "TttViewOptionRefreshCard", "TttViewOptionProvideCard", "TttViewOptionAddCards", "TttViewOptionApplicableCard" , "TttViewSelYield", "TttViewSelBeginMoveAndCopy", "TttViewSelBeginMoVeAndCopy", "TttViewSelMOVeAndSelCopy", "TttViewSelMoveAndSelCopy", "TttViewSelDelete" , 0, "TttViewProvideEnable", 0, 0, 0, . 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, I; msgTttAppChangeTracing, msgTttAppForceRepaint, iendif msgApplnit, msgAppOpen, msgAppClose, msgAppSelectAll, msgAppExport, msgControlProvideEnable, "TttDbgChangeTracing" , "TttDbgForceRepaint", 0, 0, "TttAppApplnit" , "TttAppOpen" , "TttAppClose", "TttAppSelectAll", "TttAppExport", "TttAppProvideEnable", objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, 0, 0, 0, o I; CLASS_INFO classlnfo[] = "clsTttViewTable", clsTttViewMethods, "clsTttDataTable", clsTttDataMethods, "clsTttAppTable" , clsTttAppMethods, o 0, 0, 0, I; mPRIV.H /**************************************************************************** MSG_INFO clsTttDataMethods[] msgNewDefaults, msglnit, msgFree, msgSave, msgRestore, iifdef DEBUG msgDump, iendif msgTttDataGetMetrics, msgTttDataSetMetrics, msgTttDataSetSquare, msgTttDataRead, msgUndoltem, o "TttDataNewDefaults" , "TttDatalnit", "TttDataFree" , "TttDataSave" , "TttDataRestore" , objCallAncestorBefore, objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, objCallAncestorBefore, "TttDataDump" , objCallAncestorBefore, "TttDataGetMetrics", "TttDataSetMetrics", "TttDataSetSquare", "TttDataRead" , "TttDataUndoltem" , 0, 0, 0, 0, 0, File: tttpriv;h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell ·the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY D.ISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. FOR A . PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 13 Nov 1991 18:40:22 $ $Date: This file contains things shared by various pieces of TttApp. ****************************************************************************/ I; MSG_INFO clsTttAppMethods[] msglnit, msgFree, msgSave, msgRestore, Hfdef DEBUG msgDump, msgTttAppDumpView, msgTttAppDumpDataObject, msgTttAppDumpWindowTree, I "TttApplnit" , "TttAppFree" , "TttAppSave" , "TttAppRestore" , objCallAncestorBefore, objCallAncestorAfter, objCallAncestorBefore, objCallAncestorBefore, "TttAppDump" ,. "TttDbgDumpView" , "TttDbgDumpDataObject" , "TttDbgDumpWindowTree", objCallAncestorBefore, . 0, 0, 0, iifndef TTTPRIV_INCLUDED idefine TTTPRIV_INCLUDED iifndef CLSMGR_INCLUDED iinclude iendif iifndef GEO_INCLUDED iinclude iendif iifndef SYSFONT_INCLUDED iinclude iendif TIC-TAC-TOE SAMPLE CODE w o ... fifndef TTTDATA_INCLUDED 'include fendif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Useful Macros * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 'define TttDbgHelper(str,set,flag,x) \ Dbg(if (DbgFlagGet«set), (U32) (flag») (DPrintf("%s: ",str); Debugf x;}) STATUS PASCAL TttUtilRead( OBJECT U32 P UNKNOWN 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ConunonDefines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * **************************** ConunonTypedefs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 typedef U8 TTT_VERSION, * P_TTT_VERSION; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II II II II Function definitions for routines in tttutil.c. Arguably these definitions should be in a separate header, but it's simpler just to keep them here. STATUS PASCAL TttUtilCreateScrollWin( OBJECT clientWin, P_OBJECT pScrollWin); STATUS PASCAL TttUtilCreateMenu( OBJECT OBJECT P UNKNOWN P OBJECT parent, client, pEntries, pMenu) ; STATUS PASCAL TttUtilAdjust~enu( OBJECT STATUS PASCAL TttUtilWrite ( OBJECT U32 P UNKNOWN menu); file, numBytes, pBuf); •~ STATUS PASCAL TttUtilWriteVersion( OBJECT file, TTT VERSION version); STATUS PASCAL TttUtilReadVersion( OBJECT TT.T VERSION TTT_VERSION P_TTT_VERSION file, numBytes, pBuf) ; file, minVersion, maxVersion, pVersion) ; STATUS PASCAL TttUtilGetComponents( OBJECT app, U16 getFlags, P OBJECT pScrollWin, P_OBJECT pView, P_OBJECT pDataObject) ; II II II Values for the getFlags parameter to TttUtilGetComponents. idefine tttGetScrollWin flagO #define tttGetView flag1 #define tttGetDataObject flag2 STATUS PASCAL TttUtilPostNotImplementedYet( P,-STRING· featureStr); II really P_TK~TABLE_ENTRY pEntries STATUS PASCAL TttUtilGiveUpSel( OBJECT object); STATUS PASCAL '1'ttUtilTakeSel( OBJECT object); void PASCAL TttUtilInitTextOutput( P_SYSDC_TEXT_OUTPUT p, U16 P U8 align, buf); idefine clsTttApp idefine clsTttData idefine clsTttView P_STRING PASCAL. TttUtilStrForSquareValue( TTT_SQUARE_VALUE v); II II II TTT_SQUARE_VALUE PASCAL TttUtilSquareValueForChar( char ch); 1* * * II II II II II ************************************* Debugging Routines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 Function definitions for routines in tttdbg.c Arguably these definitions should be in a separate header, but it's simpler just to keep them here. ** Global Variables and ** * * * * * * * * Defin~s A Note on global well-knows versus private well-knowns. /1 All of the UIDs used in this application are global well-knowns. /1 Strictly speaking, only the application class UID needs to be global. // If none of the other classes are referenced outside of this II application, then they can (and should) be private. II /1 // // // /1 /1 1/ // // II /1 tttAppDbgSet OxCO tttDataDbgSet. Oxel tttUtilDbgSet OxC2 tttViewDbgSet OxC3 tttViewOptsDbgSetOxC4 tttViewXferDpgSetOxC5 1/ TTTPRIV INCLUDED mAPP.H * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II II idefine idefine idefine idefine idefine ide fine 1**************************************************************************** * * * * * * * * * * * * * * * * * * * * * * * * Debug flag sets iendif 'ifdef DEBUG MsgHandler(TttDbgDumpWindowTree); MsgHandler(TttDbgDumpView); MsgHandler(TttDbgDumpDataObject); MsgHandler(TttDbgDumpDebugFlags); MsgHandler(TttDbgChangeDebugSet); MsgHandler(TttDbgChangeDebugFlag); 'endif /1 DEBUG /* * * MakeGlobalWKN(2222,I) MakeGlobalWKN(2223,1) MakeGlobalWKN(2224,1) The UIDs here are global because that's the most general and least error-prone, and this application's role as template and tutorial make generality and stability very important. However, any production application built constructed from this should consider making some of these UIDs local. And, of course, any other application (production or not) constructed from this template should get their own allocation of uids. Also allocated to tic-tac-toe, but not yet used: File: tttapp.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 13 Nov 1991 18:40:16 $ $Date: This file contains the API definition for clsTttApp. clsTttApp inherits from clsApp. ****************************************************************************1 iifndef TTTAPP_INCLUDED ide fine TTTAPP_INCLUDED 'ifndef CLSMGR_INCLUDED iinclude iendif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Conunon Typedefs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 2225 // I SAMPLE CODE nC-TAC-TOE ...o 00 .. 101 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Exported Functions * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * *1 1**************************************************************************** CIsTttApplnit returns STATUS Initializes I installs clsTttApp. This routine is only called during installation of the class. tifndef TTTAPP_INCLUDED fdefine TTTAPP_INCLUDED fifndef CLSMGR_INCLUDED tinclude 'endif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 Defines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 STATUS PASCAL CIsTttApplnit (void); 1* * * * * * * * * 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Messages for clsTttApp * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II Debug messages aren't in standard header form so that they won't * * * * * * * * * * * * * * * * * * * * * * ** * * * * * Common Typedefs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 * II show up in automatically-generated datasheets. Exported Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** II MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, MakeMsg(clsTttApp, ~ 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II ,ifdef DEBUG 'define dbgMsgStart Ox40 'define msgTttAppChangeDebugFlag 'define msgTttAppChangeDebugSet 'define msgTttAppDumpWindo'wTree 'define msgTttAppDumpDebugFlags 'define msgTttAppDumpview 'define msgTttAppDumpDataObject 'define msgTttAppChangeTracing 'define msgTttAppForceRepaint 'endif II TTTAPP INCLUDED 'endif o dbgMsgStart dbgMsgStart dbgMsgStart dbgMsgStart dbgMsgStart dbgMsgStart dbgMsgStart dbgMsgStart +0) + 1) + 2) + 3) + 4) + 5) + 6) + 7) mAPP.C 1**************************************************************************** File: tttapp.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reprodueed on all copies. THIS SAMPLE CODE IS PROVIDED "AS lSi., WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. I $Revision: ~.1 $ 13 Nov 1991 18:40:16 $ $Date: This file contains the API definition for clsTttApp. clsTttApp inherits from clsApp. ****************************************************** ~*********************I CIsTttApplnit returns STATUS Initializes I installs clsTttApp. This routine is only called during installation of the class. *1 STATUS PASCAL CIsTttApplnit (void); 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * .* * * * * * .* * Messages for clsTttApp * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II Debug messages aren't in standard header form so that they won't II show up in automatically-generated datasheets. II fifdef DEBUG fdefine dbgMsgStart Ox40 fdefine msgTttAppChangeDebugFlag MakeMsg(clsTttApp, dbgMsgStart fdefine mSgTttAppChangeDebugSet MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppDumpWindowTree MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppDumpDebugFlags MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppDumpView MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppDumpDataObject MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppChangeTracing MakeMsg(clsTttApp, dbgMsgStart . MakeMsg(clsTttApp, dbgMsgStart 'define msgTttAppForceRepaint "endif fendif II TTTAPP INCLUDED + + + + + + + + 0) 1) 2) 3) 4) . 5) 6) 7) STATUS PASCAL ClsTttDataInit (void); mDATA.H 1**************************************************************************** File: tttdata.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ $Date: 02 Dec 1991 19:07:50 $ This file contains the API definition for clsTttData. clsTttData inherits from clsObject. 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 ****************************************************************************1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II AKN II #define #define #define #define numberOfRows 5 numberOfColumns 5 tagTttDataUndoDelete tagTttDataUndoMoveCopy MakeTag (clsTttData, 0) MakeTag(clsTttData, 1) 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Common Typedefs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 typedef char TTT_SQUARE_VALUE; #define tttBlank ((TTT SQUARE VALUE)' ') #define tttX ((TTT_SQUARE_VALUE) 'X') #define tttO ( (TTT_SQUARE_VALUE) , 0' ) typedef TTT_SQUARE_VALUE TTT_SQUARES [numberOfRows] [numberOfColumns]; typedef struct TTT_DATA_METRICS TTT_SQUARES squares; U32 undoTag; TTT_DATA_METRICS, * P TTT_DATA METRICS; typedef struct TTT_DATA_NEW_ONLY { TTT_DATA_METRICS metrics; TTT_DATA_NEW_ONLY, * P_TTT_DATA_NEW_ONLY; #define tttDataNewFields \ objectNewFields \ TTT_DATA NEW_ONLY tttData; typedef struct TTT_DATA_NEW ( tttDataNewFields TTT_DATA_NEW, * P_TTT_DATA_NEW; 1***************************************************** *********************** msgNewDefaults takes P_TTT_DATA_NEW, returns STATUS category: class message Initializes the TTT DATA NEW structure to default values. typedef OBJECT TTT_DATA, * P_TTT_DATA; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Exported Functions * * msgNew takes P_TTT_DATA_NEW, returns STATUS category: class message Creates an instance of clsTttData. #ifndef TTTDATA INCLUDED #define TTTDATA_INCLUDED #ifndef CLSMGR_INCLUDED hnclude #endif * Messages for clsTttData * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** * * * * * * * * * * * *' * * * * * * * * * * * * * * * * * * * * * * * ** * *I 1**************************************************************************** ClsTttDataInit returns STATUS Initializes I installs clsTttData. This routine is only called during installation of the class. */ *1 1**************************************************************************** msgTttDataGetMetricstakesP_TTT_DATA_METRICS, returns STATUS Gets TTT DATA metrics. *1 #define msgTttDataGetMetrics 'MakeMsg(clsTttData, 1) TIC-TAC-TOE I SAMPLE CODE .... lot .. loa loa /**************************************************************************** mDATA.C msgTttDataSetMetricstakes P_TTT_DATA_METRICS, returns STATUS Sets the TTT DATA metrics. /**************************************************************************** File: tttdata.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.4 $ 07 Jan 1992 17:18:50 $ $Date: This file contains the implementation of clsTttData. */ fdefine msgTttDataSetMetrics MakeMsg(clsTttData, 2) /****************************************************.*********************** msgTttDataSetSquare takes P_TTT_DATA_SET_SQUARE, returns STATUS Sets the value of a single square. */ MakeMsg(clsTttData, 3) fdefinemsgTttDataSetSquare typedef struct { row; U16 U16 col; TTT_SQUARE_VALUE value; TTT_DATA_SET_SQUARE, * P_TTT_DATA_SET_SQUARE; ****************************************************************************/ /******************************~********************************************* msgTttDataRead takes P_TTT_DATA_READ, . returns STATUS Causes data object to try to read stationery from fileHandle. Returns stsOK if no errors occur, otherwise returns the error status. Returns stsOK even if values cannot be read from the file. The "successful" field indicates whether·a value was successfully read. If successful, the data object's state is changed and notifications are set. */ fdefine msgTttDataRead typedef struct TTT_DATA_READ OBJECT fileHandle; BOOLEAN successful; TTT_DATA_READ, * P_TTT_DATA_READ; MakeMsg(clsTttData, 4) /**************************************************************************** msgTttDataChanged takes P_TTT_DATA_CHANGED or nothing, returns nothing category: observer notification Sent to observers when the value changes. IfpArgs is pNull, receiver should assume that everything has changed. */ MakeMsg(clsTttData, 5) fdefine msgTttDataChanged typedef struct ( U16 row; U16 col; TTT_DATA_CHANGED, * P_TTT_DATA_CHANGED; 'endif. // TTTDATA_INCLUDED \ 'ifndef DEBUG_INCLUDED 'include fendif .ifndef RESFILE_INCLUDED 'include ·fendif ,ifndef TTTDATA_INCLUDED iinclude fendif fifndef TTTPRIV_INCLUDED 'include 'endif iifndef OSHEAP_INCLUDED finclude fendif ,ifndef UNDO_INCLUDED 'include fendif iinclude finclude 'include /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ typedef struct TTT_DATA_INST TTT DATA METRICS metrics; TTT_DATA_INST, * P_TTT_DATA_INST, * * PP_TTT_DATA_INST; II II II II II II CURRENT VERSION is the file format version written by this implementation. MIN_VERSION is the minimum file format version readable by this implementation. MAX VERSION is the maximum file format version readable by this implementation. fdefine CURRENT_VERSION fdefine MIN_VERSION fdefine MAX_VERSION °° ° typedef struct TTT_DATA_FILED_O TTT_SQUARES squares; TTT_DATA_FILED_O, * P_TTT_DATA_FILED_O; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** TttDataFiledDataOFromInstData Computes filed data from instance data. ****************************************************** **********************1 STATIC void PASCAL TttDataFiledDataOFromInstData( P_TTT_DATA_INST pInst, P_TTT_DATA_FILED 0 pFiled) memcpy(pFiled->squares, pInst->metrics.squares, sizeof(pFiled->squares»; } 1* TttDataFiledDataOFromInstData *1 1***************************************************** *********************** TttDataInstDataFromFiledDataO Computes instance data from filed data. ****************************************************** **********************1 STATIC void PASCAL TttDataInstDataFromFiledDataO( P_TTT_DATA_FILED_O pFiled, P TTT DATA INST pInst} memcpy(pInst->metrics.squares, pFiled->squares, sizeof(pInst->metrics.squares»; } 1* TttDataInstDataFromFiledDataO *1 1***************************************************** *********************** TttDataNotifyObservers I SAMPLE CODE Sends notifications. ****************************************************** **********************1 fdefine DbgTttDataNotifyObservers(x) \ TttDbgHelper (~'TttDataNotifyObservers" ,tttDataDbgSet, Ox!, x) STATIC STATUS PASCAL TttDataNotifyObservers( self, OBJECT pArgs) P ARGS nobs; OBJ_NOTIFY_OBSERVERS s; STATUS DbgTttDataNotifyObservers « .... » nobs.msg = msgTttDataChanged; nobs.pArgs = pArgs; nobs.lenSend = SizeOf(TTT_DATA_CHANGED}; ObjCallJmp(msgNotifyObservers, self, &nobs, s, Error); DbgTttDataNotifyObservers «"return stsOK"» return stsOK; Error: DbgTttDataNotifyObservers «"Error; return Ox%lx", s» return s; } 1* TttDataNotifyObservers *1 1***************************************************** *********************** TttDataRecordStateForUndo Records current state with undo manager. Assumes that a transaction is already open. ****************************************************** **********************1 fdefine DbgTttDataRecordStateForUndo(x) \ TttDbgHelper("TttDataRecordStateForUndo",tttDataDbgSet,Ox2,x) STATIC STATUS PASCAL TttDataRecordStateForUndo( OBJECT self, undoTag, TAG pData) PP TTT DATA INST item; UNDO ITEM STATUS s; DbgTttDataRecordStateForUndo ( (.... ) ) ObjCallJmp(msgUndoBegin, theUndoManager, (P_ARGS)undoTag, s, Error); item. object = self; item. subclass = clsTttData; item. flags = 0; item.pData = &«*pData)->metrics); item.dataSize = SizeOf«(*pData)->metrics»; ObjCallJmp(msgUndoAddItem, theUndoManager, &item, s, Error); ObjCallJmp(msgUndoEnd, theUndoManager, pNull, s, Error); TIC-TAC-TOE .. lit W ...... DbgTttDataRecordStateForUndo«"return'stsOK"» return stsOK; Error: DbgTttDataRecordStateForUndo ( ("Error; return Ox%1x", s» return s; } 1* TttDataRecordStateForUndo *1 STATIC STATUS PASCAL TttDataPrivSetSquare( OBJECT P_TTT_DATA_SET_SQUARE PP TTT DATA INST BOOLEAN s; STATUS DbgTttDataPrivSetMetrics «""» II II Perhaps record undo information II if (recordUndo) ( StsJmp (TttDataRecordStateForUndo (self, pArgs->undoTag, pData)" s, Error); II self, pArgs, pData, recordUndo) STATUS s; DbgTttDataPrivSetSquare ( ("") ) 1***************************************************** *********************** TttDataPrivSetMetrics Sets metrics and (optionally) records information needed to undo the set. Assumes an undo transaction is open. ****************************************************** **********************1 tdefine DbgTttDataPrivSetMetrics(x) \ TttDbgHelper ("TttDataPrivSetMetrics" , tttDataDbgSet, Ox4 ,x) STATIC STATUS PASCAL TttDataPrivSetMetrics( self, OBJECT P TTT DATA METRICS pArgs, PP_TTT_DATA_INST pData, BOOLEAN recordUndo) ..,' II II II Perhaps record undo information if (recordUndo) { St~Jmp (.TttDataRecordStateForUndo (self, pNull, pData), s, Error); } II I I Change data. II . (*pData)->metrics.squares[pArgs->row] [pArgs->col] DbgTttDataPrivSetSquare «"returns stsOK"» return stsOK; Error: DbgTttDataPrivSetSquare «"Error; return Ox%1x", s» return s; } 1* TttDataPrivSetSquare *1 pArgs->value; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ~ Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** II Change data. TttDataNewDefaults II (*pData)->metrics = *pArgs; DbgTttDataPrivSetMetrics {("returns stsOK"» return stsOK; Error: DbgTttDataPrivSetMetrics «"Error; return Ox%1x", s» return s; } 1* TttDataPrivSetMetrics *1 1***************************************************** *********************** TttDataPrivSetSquare Sets a square and (optionally) records information needed to undo the set. Assumes an undo transaction is open. ****************************************************** **********************1 ,define DbgTttDataPrivSetSquare(x) \ TttDbgHelper("TttDataPrivSetSquare",tttDataDbgSet,Ox8,x) Respond to msgNewDefaults. **********************************************~******* **********************1 'define DbgTttDataNewDefaults(x) \ TttDbgHelper("TttDataNewDefaults",tttDataDbgSet,Ox10,x) MsgHandlerWithTypes(TttDataNewDefaults, P_TTT_DATA_NEW, PP_TTT_DATA_INST) ( U16 row; U16 col; DbgTttDataNewDefaults«""» for (row=O; row<3; row++) { for (col=O; col<3; col++). { pArgs->tttData.metrics.squares[row] [col] } pArgs->tttData.metrics.undoTag =~ tttBlank; DbgTttDataNewDefaults ( (" return stsOK")) return stsOK; MsgHandlerParametersNoWarning; } 1* TttDataNewDefaults *1 ****************************************************************************/ #define DbgTttDataFree(x) \ TttDbgHelper("TttDataFree",tttDataDbgSet,Ox40,x) MsgHandlerWithTypes(TttDataFree, P_ARGS, PP_TTT_DATA_INST) { /**************************************************************************** TttDataInit Initialize instance data of new object. Note: clsmgr has already initialized instance data to zeros. ****************************************************************************/ #define DbgTttDataInit(x) \ TttDbgHelper ("TttDataInit",tttDataDbgSet, Ox20,x) MsgHandlerWithTypes(TttDataInit, P_TTT_DATA_NEW, PP_TTT_DATA_INST) { P_TTT_DATA_INST pInst; STATUS s; DbgTttDataInit (("")) II II II /**************************************************************************** TttDataSave Initialize for error recovery. pInst II II II STATUS s; DbgTttDataFree ( ("") ) StsJmp(OSHeapBlockFree(*pData), s, Error); DbgTttDataFree (("return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttDataFree (("Error; return Ox%lx", s)) return s; I 1* TttDataFree *1 = pNull; Allocate, initialize, and record instance data. Save self to a file. ****************************************************************************/ #define DbgTttDataSave(x) \ TttDbgHelper("TttDataSave",tttDataDbgSet,Ox80,x) MsgHandlerWithTypes(TttDataSave, P_OBJ_SAVE, PP_TTT_DATA_INST) { StsJmp(OSHeapBlockAlloc(osProcessHeapId, SizeOf(*pInst), &pInst), \ s, Error); pInst->metrics = pArgs->tttData.metrics; ObjectWrite(self, ctx, &pInst); DbgTttDataInit (("return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: i f (pInst) { OSHeapBlockFree(pInst); DbgTttDataInit (("Error; returns Ox%lx", s)) return s; I 1* TttDataInit *1 TTT DATA FILED 0 filed; STATUS s; DbgTttDataSave ( ('''') ) StsJmp (TttUtilWriteVersion (pArgs->file, CURRENT VERSION), s, Error); TttDataFiledDataOFromInstData(*pData, &filed); StsJmp(TttUtilWrite(pArgs->file, SizeOf(filed) , &filed), s, Error); DbgTttDataSave (("return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttDataSave (("Error; return Ox%lx", s)) return s; } 1* TttDataSave *1 1***************************************************** *********************** 1***************************************************** *********************** TttDataFree Respond to msgFree. Note: Always return stsOK, even if a problem occurs. This is (1) because there's nothing useful to do if a problem occurs anyhow and (2) because the ancestor is called after this function if and only if stsOK is returned, and it's important that the ancestor get called. I TttDataRestore Restore self from a file. Note: clsmgr has already initialized instance data to zeros. ****************************************************** **********************1 #define DbgTttDataRestore(x) \ TttDbgHelper("TttDataRestore",tttDataDbgSet,Ox100,x) MsgHandlerWithTypes(TttDataRestore, P_OBJ_RESTORE, PP_TTT_DATA_INST) TlC-TAC-TOE .. hi VI SAMPLE CODE .. lot TttUtilStrForSquareValue«*pData)->metrics.squares[2) [0)), TttUtilStrForSquareValue«*pData)->metrics.squares[2) [1J), TttUtilStrForSquareValue «*pData)->metrics.squares [2) [2))); return stsOK; MsgHandlerParametersNoWarning; I 1* TttDataDump *1 #endif II DEBUG P TTT DATA INST pInst; filed; TTT- DATA- FILED- 0 TTT VERSION version; STATUS s; DbgTttDataRestore «nn)) II II II Initialize for error recovery. /**************************************************************************** pInst II II II II II II II 0\ = pNull; TttDataGetMetrics ****************************************************************************/ Read version, then read filed data. (Currently there's only only one legitimate file format, so no checking of the version need be done.) #define DbgTttDataGetMetrics(x) \ TttDbgHelper (nTttDataGetMetrics n, tttDataDbgSet, Ox200, x) MsgHand1erWithTypes(TttDataGetMetrics, P_TTT_DATA_METRICS, PP_TTT_DATA_INST) { The allocate instance data and convert filed data. StsJmp(TttutilReadVersion(pArgs->:Eile, MIN-,-VERSION, MAX_VERSION, \ &version), s, Error); StsJmp(TttUtilRead (pArgs->file, Siz.eOf (filed), &filed), s, Error); StsJmp(OSHeapBlockAlloc(osProcessHeapId, SizeOf(*pInst), &pInst), \ s, Error); TttDataInstDataFromFiledDataO (&filed, pInst); ObjectWrite(self, ctx, &pInst); DbgTttDataRestore ( (n returns stsOK n)) return stsOK; MsgHandlerParametersNoWarning; Error: if (pInst) { OSHeapBlockFree(pInst); DbgTttDataRestore «nError; returns Ox%lx n, s)) return s; I 1* TttDataRestore *1 /**************~************************************** *********************** TttDataDump Respond to msgDump. **************************************j,************** ***********************1 Ufdef DEBUG MsgHandlerWithTypes(TttDataDump, P_ARGS, PP_TTT_DATA_INST) { Debugf(nTttDataDump: [%s %s %s) [%s %s %s) [%s %s %s)n, TttUtilStrForSquareValue«*pData)->metrics.squares[O] TttUtilStrForSquareValue«*pData)->metrics.squares[O) TttUtilStrForSquareValue«*pData)->metrics.squares[O) TttUtilStrForSquareValue«*pData)->metrics.squares[1) TttUtilStrForSquareValue «*pData) ->metrics. squares [1) TttutilStrForSquareValue «*pData) ->metrics. squares [1) [0)), [1)), [2)), [0)), [1)) , [2)), DbgTttDataGetMetrics ( (n n) ) *pArgs = (*pData)->metrics; DbgTttDataGetMetrics ( (nreturns stsOK n)) return stsOK; MsgHandlerParametersNoWarning; I 1* TttDataGetMetrics *1 /**************************************************************************** TttDataSetMetrics ****************************************************************************/ #define DbgTttDataSetMetrics(x) \ TttDbgHelper(nTttDataSetMetrics n,tttDataDbgSet,Ox400,x) MsgHandlerWithTypes(TttDataSetMetrics, P_TTT_DATA_METRICS, PP_TTT_DATA_INST) { BOOLEAN transactionOpen; STATUS s; DbgTttDataSetMetrics «nn)) II Steps: II * Initialize for error recovery. II * Begin the undo transaction. II * Change the data and record undo information. II * End the undo transaction. II * Notify observers. II transactionOpen = false; ObjCallJmp(msgUndoBegin, theUndoManager, (P_ARGS)pArgs->undoTag, s, Error); transactionOpen = true; StsJmp(TttDataPrivSetMetrics(self, pArgs, pData, true), s, Error); ObjCallJmp(msgUndoEnd, theUndoManager, pNull, s, Error); transactionOpen = false; StsJmp(TttDataNotifyObservers(self, pNull), s, Error); DbgTttDataSetMetrics ( (n returns stsOK n)) return stsOK; MsgHandlerParametersNoWarning; II II Error: if (transactionOpen) ( ObjCallJmp(msgUndoEnd, theUndoManager, pNull, s, Error); II II II II ' , FIXME: This should abort, not end, the transaction. The abort functionality should be available in M4.B. DbgTttDataSetMetrics (("Error; return Ox%lx", s» return S; } 1* TttDataSetMetrics *1 The abort functionality should be available in M4.B. DbgTttDataSetSquare (("Error; return Ox%lx", s» return s; } 1* TttDataSetSquare *1 1***************************************************** *********************** TttDataRead . Handles msgTttDataRead ********************************~********************* **********************1 1***************************************************** *********************** TttDataSetSquare Handles both msgTttDataSetMetrics and msgTttDataSetSquare ****************************************************** **********************1 idefine DbgTttDataSetSquare(x) \ TttDbgHelper("TttDataSetSquare",tttDataDbgSet,OxBOO,x) MsgHandlerWithTypes(TttDataSetSquare, P_TTT_DATA_SET_SQUARE, PP_TTT_DATA_INST) ( changed; TTT DATA CHANGED BOOLEAN transactionOpen; STATUS S; DbgTttDataSetSquare(("row=%ld col=%ld value=%s", \ (U32) (pArgs->row), (U32) (pArgs->col), \ TttUtilStrForSquareValue(pArgs->value») II Steps: II * Initialize for error recovery. II * Begin the undo transaction. II * Change the data and record undo information. II * End the undo transaction. II * Notify observers. /! transactionOpen = false; ObjCallJmp(msgUndoBegin, theUndoManager, pNull, s, Error); transactionOpen = true; StsJmp (TttDataPrivSetSquare (s,elf, pArgs, pData, true), s, Error); ObjCallJmp(msgUndoEnd, theUndoManager, pNull, s, Error); transactionOpen = false; changed. row = pArgs->row; changed. col = pArgs->col; StsJmp(TttDataNotifyObservers(self, &changed), s, Error); DbgTttDataSetSquare (("returns stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: if (transactionOpen) ( ObjCallJmp(msgUndoEnd, theUndoManager, pNull, s, Error); /! II FIXME: This should abort, not end, the transaction. I ide fine DbgTttDataRead(x) \ TttDbgHelper("TttDataRead",tttDataDbgSet,OxIOOO,x) ide fine N_BYTES 9 MsgHandlerWithTypes(TttDataRead, P_TTT_DATA_READ, PP~TTT_DATA_INST) { STREAM_READ_WRITE read; buf[N_BYTES+l]; U8 row; U16 col; U16 STATUS S; DbgTttDataRead ( ('''') ) II II II Read in the 9 bytes that must be present. If there are fewer than 9 bytes, treat the attempt to read as a failure. /! read.numBytes = SizeOf(buf) - 1; read.pBuf = buf; ObjCallJmp(msgStreamRead, pArgs->fileHandle, &r~ad, s, Error); buf[read.count] = '\0'; DbgTttDataRead(("read.count=%ld buf=<%s>", (U32)read.count,buf» /! II Now convert the buffer contents to reasonable square values. /! for (row=O; row<3; row++) for (col=O; col<3; col++) int Chi ch = buf[(row*3)+col]; i f ((ch = 'X') OR (ch == 'x'» { (*pData)->metrics.squares[row] [col] = tttX; else i f ((ch == '0') OR (ch == '0'» { (*pData)->metrics.squares[row] [col] = tttO; else { (*pData)->metrics.squares[row] [col] = tttBlank; StsJmp(TttDataNotifyObservers(self, pNull), s, Error); pArgs->successful = true; TIC-TAC-TOE, SAMPLE CODE ...... ~ ..... DbgTttDataRead( ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttDataRead ( ("Error; return Ox%lx", s) ) return s; } /* TttDataRead */ /**************************************************************************** TttDataUndoItem new.object.key 0; new.cls.pMsg clsTttDataTable; new.cls.ancestor clsObject; new.cls.size SizeOf(P_TTT_DATA_INST); new. cIs. newArgsSize SizeOf(TTT_DATA_NEW); ObjCallJmp(msgNew, clsClass, &new, s, Error); return stsOK; Error: return s; } /* CIsTttDataInit */ ****************************************************************************/ 'define DbgTttDataUndoItem(x) \ TttDbgHelper("TttDataUndoItem",tttDataDbgSet,Ox2000,x) MsgHandlerWithTypes(TttDataUndoItem, P_UNDO_ITEM, PP_TTT_DATA_INST) mDBG.C /*****************************************~********************************** File: tttdbg.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ¥D GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 13 Nov 1991 18:38:00 $ $Date: This file contains the implementation of miscellaneous debugging routines used by TttApp. The interfaces to these routines are in tttpriv.h. { STATUS s; DbgTttDataUndoItem( ("")) if (pArgs->subclass != clsTttData) DbgTttDataUndoItem( ("not clsTttData; give to ancestor"» return ObjectCallAncestorCtx(ctx); StsJmp(TttDataPrivSetMetrics(self, pArgs->pData, pData, false), s, Error); StsJmp(TttDataNotifyOhservers(self, pNull), s, Error); DbgTttDataUndoItem( ("returns stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttDataUndoItem ( ("Error; return Ox%lx", s) ) return s; , } /* TttDataUndoItem */ ****************************************************************************/ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Installation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**************************************************************************** CIsTttDataInit Install the class. ****************************************************************************/ STATUS PASCAL CIsTttDataInit (void) ( CLASS NEW new; s; STATUS ObjCallJmp(msgNewDefaults, clsClass, &new, s, Error); new.object.uid = clsTttData; .ifndef WIN_INCLUDED tinclude tendif tifndef VIEW INCLUDED 'include tendif 'ifndef DEBUG INCLUDED tinclude lendif 'ifndef OS_INCLUDED linclude lendif tifndef APP_INCLUDED linclude tendif lifndef FRAME_INCLUDED linclude 'endif tifndef CLSMGR_INCLUDED • iinclude iendif iifndef TTTPRIV_INCLUDED iinclude. iendif iifndef TTTDATA_INCLUDED iinclude iendif iinclude iinclude return s; } 1* TttDbgDumpWindowTree *1 iendif I ( DEBUG 1***************************************************** *********************** TttDbgDumpView Self must be app .. ****************************************************** **********************1 iifdef DEBUG MsgHandler(TttDbgDumpView) 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * D~fines, * Types, Globals, Etc * * * * * * * * * * * * * * * *.* * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 ( view; VIEW STATUS s; StsJmp(TttUtilGetComponents(self, tttGetView, objNull, &view, objNull), \ s, Error); ObjCallJrnp(msgDump, view, pNull, s, Error); return stsOK; MsgHandlerParametersNoWarning; Error: return s; } 1* TttDbgDumpView *1 iendif I I DEBUG 1***************************************************** *********************** TttDbgDumpWindowTree Invoked from debugging menu item to display window tree. Self must be app. If the user picked the first command in the submenu, pArgs will be 0, and this handler will dump from the app's rnainWin. Otherwise, it will dump from the app's rnainWin's clientWin. ****************************************************** **********************1 tifdef DEBUG MsgHandler(TttDbgDumpWindowTree) { OBJECT root; APP.;,.METRICS am; STATUS s; if « (U32) (pArgs)) == 0) ( ObjCallJrnp(msgAppGetMetrics, self, &am, s, Error); root = am.mainWin; else ( StsJmp(TttUtilGetCornponents(self, tttGetView, objNull, &root, \ objNull), s, Error); 1***************************************************** *********************** TttDbgDumpDataObject Self must be app. ****************************************************** **********************1 tifdef DEBUG MsgHandler(TttDbgDumpDataObject) ( OBJECT dataObject; STATUS s; StsJmp(TttUtilGetComponents(self, tttGetDataObject, objNull, objNull, \ &dataObject), s, Error); ObjCallJmp(msgDump, dataObject, pNull, s, Error); return stsOK; MsgHandlerParametersNoWarning; Error: return s; } 1* TttDbgDumpDataObject *1 iendif II DEBUG ObjCallJmp (msgWinDumpTree, root, pNull, s, Error); return stsOK; MsgHandlerParametersNoWarning; Error: I TlC-TAC-TOE .. lot -0 SAMPLE CODE 1**************************************************************************** TttDbgChangeTracing ****************************************************************************1 itifdef DEBUG MsgHandler(TttDbgChangeTracing) Error: return s; I 1* TttDbgForceRepaint *1 itendif I I DEBUG mIPAD.C ( OBJECT view; dataObject; OBJECT target; OBJECT args = (U32)pArgs; U32 targetArg = LowUI6(args); U16 turnTraceOn; BOOLEAN STATUS s; StsJmp(TttUtilGetComponents(self, tttGetView I tttGetDataObject, objNull, \ &view, &dataObject), s, Error); turnTraceOn = (HighUl6 (args) == 1); if (targetArg == 0) { Debugf("Setting tracing of app %s", turnTraceOn? "On" "Off"); target = self; else if (targetArg == 1) { Debugf("Setting tracing of view %s", turnTraceOn ? "On" "Off"); target = view; else { Debugf("Setting tracing of dataObject %s", turnTraceOn ? "On" "Off"); target = dataObject; ObjCallWarn (msgTrace, target, IP._ARGS) turnTraceOn); return stsOK; MsgHandlerParametersNoWarning; Error: return s; I 1* TttDbgChangeTracing *1 itendif I I DEBUG I************************************i'*************** ************************ TttDbgForceRepaint ****************************************************************************1 itifdef DEBUG MsgHandler(TttDbgForceRepaint) { OBJECT view; STATUS s; StsJmp (TttUtilGetComponents (self" tttGetView, objNull, &view, objNull), \ s, Error); ObjCallJmp(msgWinDirtyRect, view, pNull, s, Error); ObjCallJmp(msgWinUpdate, view, pNull, s, Error); return stsOK; MsgHandlerParametersNoWarning; /**************************************************************************** File: tttipad.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ 13 Nov 1991 18:38:04 $ $Date: ***************************************************************************1 itifndef TTTVIEW_INCLUDED itinclude lendif II AKN - Function to create an insertion pad STATUS TttViewCreateIPad( P GWIN_GESTURE pGesture, P_TTT_VIEW_INST pInst~ VIEW self) WIn; WIN METRICS WIN METRICS myMetrics; STATUS s; IP NEW ipNew; P XTEMPLATE pNewTemplate; XLATE NEW xNewTrans; U16 xlateFlags; i; Ul6 OBJECT dataObject; TTT DATA METRICS dm; II II Get my data object and metrics to get existing string II ObjCa11Jmp(msgViewGetDataObject, self, &dataObject, s, Error); ObjCallJmp(msgTttDataGetMetrics, dataObject, &dm, s, Error); II II compute and set the hit row and col II ...... o ObjCallJmp(msgWinGetMetrics, self, &myMetrics, s, Error); for(i= 1; i <= numberOfColumns; iff) { if(pGesture->hotPoint.x < (i * (myMetrics.bounds.size.w I numberOfColumns))) { pInst->currentCell.col = i - 1; break; forti = 1; i <= numberOfRows; i++) { if(pGesture->hotPoint.y < (i * (myMetrics.bounds.size.h pInst->currentCel1. row = i - I ; break; II II II I II II II ObjectCall(msgNewDefaults, clsIP, &ipNew); ipNew.border.style.resize true; ipNew.ip.style.buttonType ipsBottomButtons; ipNew.ip.style.displayType ipsCharBoxButtons; ipNew.ip.style.embeddedLook false; ipNew.ip.style.freeAfter true; ipNew.ip.style.rnodal ipsNoMode; ipNew.ip.style.takeGrab true; ipNew.ip.style.takeFocus true; ipNew.ip.style.delayed false; ipNew.ip.rows = 1; ipNew.ip.cols = 10; ipNew.ip.maxLen = 10; ipNew.win.bounds.origin.x = pGesture->hotPoint.x - 135; ipNew.win.bounds.origin.y = pGesture->hotPoint.y - 45; ipNew.win.bounds.size.w = 270; ipNew.win.bounds.size.h = 90; II set the listener field for notifications ipNew.ip.client = self; II set the initial string from the model ipNew.ip.pString = " "; ipNew.ip.pString[O] = dm.squares[pInst->currentCell.row] [pInst->currentCell.col]; II Create a translator for the insertion pad ObjectCall(msgNewDefaults, clsXText, &xNewTrans); II Create a template for the new translator if (s = XTemplateCompile(xtmTypeCharList, 0, "ABCDEFGHIJRLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxYz01234567890+-x*l. ()A=", osProcessHeapId, &pNewTemplate) < stsOK ) { return s; give char list veto power ObjCallRet(msgXlateGetFlags, xNewTrans.object.uid, &xlateFlags, s); xlateFlags 1= xTemplateVeto 1 spaceDisable; ObjCallRet(msgXlateSetFlags, xNewTrans.object.uid, (P_ARGS)xlateFlags, s); ObjCal1Ret(msgNew, clsIP, &ipNew, s); II create the pad numberOfRows))) { Create the insertion pad window and insert it as a child of my view I xNewTrans.xlate.pTernplate = pNewTemplate; ObjCallRet(msgNew, ·clsXText, &xNewTrans, s); ipNew.ip.xlate.translator = xNewTrans.object.uid; II II II Insert pad into the window wrn.parent self; wrn.options = wsPosTop; ObjCallRet(msgWinInsert, ipNew.object.uid, &wrn, s); ObjectCall(msgWinSetLayoutDirty, ipNew.object.uid, &wrn); ObjCallRet(msgWinLayout, ipNew.object.uid, &wrn, s); return stsOK; Error: if (ipNew.object.uid) ObjCallWarn(msgDestroy, ipNew. object. uid, pNull); return s; 1* CreateInsertWindow *1 1**************************************************************************** TttViewGestureSetCell Handles all gestures that set the value of a single square. ****************************************************************************1 tdefine DbgTttViewGestureSetCell(x) \ TttDbgHelper("TttViewGestureSetSquare",tttViewDbgSet, Ox 2,x) STATIC STATUS LOCAL TttViewGestureSetCell(VIEW self, P_TTT_VIEW_INST pInst) { OBJECT dataObject; STATUS s; ObjCallJmp(msgViewGetDataObject, self, &dataObject, s, Error); ObjCallJmp(msgTttDataSetSquare, dataObject, &pInst->currentCell, s, Error); DbgTttViewGestureSetCell ( ("return stsOK")) return stsOK; Error: DbgTttViewGestureSetCell «"Error; returns Ox%lx", s)) return s; I 1* TttViewGestureSetCell *1 1* AKN - Method Support for insertion pad TlC-TAC-TOE SAMPLE CODE .. lot lot 1***************************************************** *********************** TttViewGetIPadData Method for processing the translated data reported by an IP. Used for msgIPDataAvailable and DlsgFieldModified ****************************************************** **********************1 MsgHandlerWithTypes(TttViewGetIPadData, OBJECT, PP_TTT_VIEW_INST} { IP STYLE ipStyle; IP STRING ipStr; pStr; P STRING P STRING pCopy; STATUS s; P_TTT_VIEW_INST pInst; pInst = *pData; ipStr.len = 20; , ipStr.pString = " ObjCaIIRet(msgIPGetXlateString, pArgs, &ipStr, s}; ObjCaIIRet(msgIPSetString, pArgs, ipStr.pString, s}; pInst ->currentCell. value = (TTT_SQUARE_VALUE) ipStr. pSt ring [0] ; StsJmp (TttViewGestureSetCell (self, pInst), s, Error}; return (stsOK); Error: return (s); II MsgHandlerParameterNoWarning; I I I TttViewGetIPadData II. mMBAR.C /*************************************~r************** ************************ File: tttmbar.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED W~ITIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF ~mRCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR I~IDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY. TO USE THIS SAMPI.E CODE. $Revision: 1.1 $ $Date: 13 Nov 1991 18:38:06 $ This file contains some of the implementation of TttApp's menu bar. ****************************************************** *********************1 iifndef TKTABLE_INCLUDED iinclude iendif iifndef TTTPRIV INCLUDED loa loa loa iinclude iendif iifndef TTTAPP INCLUDED iinclude iendif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 bfdef DEBUG static TK TABLE_ENTRY dumpTreeMenu[] = { {"From Frame", msgTttAppDumpWindowTree, 0 I, {"From View", msgTttAppDumpWindowTree, 1 I, {pNulll I; II II II II II II Arguably this could be improved by showing making three exclusive choices, 'and displaying the current state in the menu. But there's no way to get the trace state from the c1smgr, and even if there was, it's not terribly important to be that careful with debugging code. static TK TABLE ENTRY traceMenu[] = { {"Trace App On", msgTttAppChangeTracing, {"Trace App Off", msgTttAppChangeTracing, {"Trace View On", msgTttAppChangeTracing, {"Trace View Off", msgTttAppChangeTracing, {"Trace Data On", msgTttAppChangeTracing, {"Trace Data Off", msgTttAppChangeTracing, {pNulll MakeU32{0,1)}, MakeU32{0,O}}, MakeU32{1,1) I, MakeU32 {1, O} I, MakeU32(2,l}}, MakeU32(2,O)I, I; static TK_TABLE_ENTRY debugMenu[] = { {"Dump View", msgTttAppDumpView, 01, {"Dump Data", msgTttAppDumpDataObject, O} , {"Dump App", msgDump, O} , {"Dump Window Tree", {U32)dumpTreeMenu, 0, 0, tkMenuPullRightl, {"Trace", {U32}traceMenu, 0, 0, tkMenuPullRight I tkBorderEdgeTopl, {"Force Repaint", msgTttAppForceRepaint, 0, 0, tkBorderEdgeTop}, {pNulll I; iendif II DEBUG TK TABLE ENTRY tttReaIMenuBar[] bfdef DEBUG {"Debug" , (U32}debugMenu; 0, 0, tkMenuPullDownl, iendif {pNulll I; II II II II II II II Why two variables, one "real" and onen0t real? The reason is that including including tktable.h (which is where TK_TABLE_ENTRY is defined) defines do many symbols that the compiler is overwhelmed. This bit of trickery allows clients to refer to tttMenuBar without including tktable.h P UNKNOWN tttMenuBar const CLS_SYM_OBJ tttObjSymbols[] clsTttApp, "clsTttApp", clsTttData, "clsTttData", clsTttView, "clsTttView", 0, 0"); STATUS EXPORTED TttSymbolsInit(void) ( . return ClsSymbolsInit( "ttt" ; tttObjSymbols, tttMsgSymbols, tttStsSymbols); (P_UNKNOWN)tttReaIMenuBar; s_m.c II II II II WARNING: File generated by GO utility: Gensyms. Do not edit. Instead, run gensyms again. itifdef DEBUG itinclude itinclude itinclude itinclude itinclude const CLS_SYM_STS tttStsSymbols[] 0, 0 j; const CLS_SYM_MSG tttMsgSymbols[] msgTt tAppChangeDebugF lag, "msgTt tAppChangeDebugF lag" , msgTttAppChangeDebugSet, "msgTttAppChangeDebugSet", msgTttAppDumpWindowTree, "msgTttAppDumpWindowTree", msgTttAppDumpDebugF lags, "msgTttAppDumpDebugF lags" , msgTttAppDumpView, "msgTttAppDumpView", msgTttAppDumpDataObject, "msgTttAppDumpDataObject", msgTttAppChangeTracing, "msgTttAppChangeTracing", msgTttAppForceRepaint, "msgTttAppForceRepaint", tagTttDataUndoDelete, "tagTttDataUndoDelete", tagTttDataUndoMoveCopy, "tagTttDataUndoMoveCopy", msgTttDataGetMetrics, "msgTttDataGetMetrics", msgTttDataSetMetrics, "msgTttDataSetMetrics", msgTttDataSetSquare, "msgTttDataSetSquare", msgTttDataRead, "msgTttDataRead", msgTttDataChanged, "msgTttDataChanged", tagTttViewOptionSheet, "tagTttViewOptionSheet", tagTttViewCard, "tagTttViewCard", tagCardLineThickness, "tagCardLineThickness" , tagTttQHelpForView, "tagTttQHelpForView", tagTttQHelpForLineCtrl, "tagTttQHelpForLineCtrl", msgTttViewGetMetrics, "msgTttViewGetMetrics" , msgTttViewSetMetrics, "msgTttViewSetMetrics", msgTttViewToggleSel, "msgTttViewToggleSel" , msgTttViewTakeSel, "msgTttViewTakeSel" , 0, OJ; I itelse #include STATUS EXPORTED TttSymbolsInit(void) { return stsOK; itendif mUTIL.C 1**************************************************************************** File: tttutil.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ $Date: 13 Nov 1991 18:38:08 $ This file contains the implementation of miscellaneous utility routines used by TttApp. The interfaces to these routines are in tttpriv.h. ****************************************************************************1 itifndef GO_INCLUDED #include itendif itifndef FS_INCLUDED #include itendif itifndef SWIN INCLUDED itinclude itendif itifndef MENU_INCLUDED TlC-TAC-TOE SAMPLE CODE ht ht Cot 'include iendif 'ifndef APP_INCLUDED 'include iendif tifndef FRAME_INCLUDED 'include iendif 'ifndef TTTPRIV_INCLUDED 'include tendif tifndef SWIN_INCLUDED linclude 'endif tifndef VIEW_INCLUDED 'include lendif tifndef NOTE_INCLUDED 'include 'endif 'ifndef DEBUG_INCLUDED 'include 'endif tifndef CLSMGR_INCLUDED 'include 'endif 'ifndef SEL_INCLUDED 'include 'endif 'ifndef APPTAG_INCLUDED 'include 'endif 'include 'include 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * ~ * * * * * * * * * * * * * * * * * * * * * *1 1***************************************************** *********************** TttUtilStrForSquareValue ****************************************************** **********************1 /I II Output is nicer if all strings have the same length. Don't worry /I about the "Unknown" string -- it should never be output anyhow. /I static P_STRING valueStrings[] = "_n, "xn, "a"., "Unknown"}; P STRING PASCAL TttUtilStrForSquareValue( TTT_SQUARE_VALUE v) if (v == tttBlank) return valueStrings[O]; else if (v == tttX) { return valueStrings[l]; else if (v == tttO) { return valueStrings[2]; else { return valueStrings[3]; } 1* TttUtilStrForSquareValue *1 1***************************************************** *********************** TttUtilSquareValueForChar ****************************************************** **********************1 TTT_SQUARE_VALUE PASCAL TttUtilSquareValueForChar( char chI return «TTT_SQUARE_VALUE) ch); 1* TttUtilSquareValueForChar *1 1***************************************************** *********************** TttUtilInsertUnique Utility routine that inserts and element into an array if it's not already in the array. Assumes enough space in the array. ****************************************************** **********************1 'define DbgTttUtiIInsertUnique(x) , TttDbgHelper("TttUtilInsertUnique",tttUtiIDbgSet,Oxl,x) STATIC void PASCAL TttUtilInsertUnique( P_U32 pValues, P_U16 pCount, II In: number of elements in pyalues II Out: new number of elements in pValties new) U32 U16 i; DbgTttUtilInsertUnique«"*pCount=%ld new=%ld=Ox%lx", (U32) (*pCount), , (U32) new, (U32)new)) for (i=O; i<*pCount; i++) { if (pValues[i] == new) { DbgTttUtiIInsertUnique«nfound it at %ld; returning", (U32)i)) return; pValues[*pCount] = new; *pCount = *pCount + 1; DbgTttUtiIInsertUnique«ndidn't find it; new count=%ld", (U32) (*pCount»)) ...... ... } /* TttUtilInsertUnique */ /**************************************************************************** TttUtilCreateScrollWin This is in a utility routine rather than in the caller because including swin.h brings in too many symbols. The example of scrolling used here is slightly artificial. This tells the scroll window.that it can expand the Tic-Tac-Toe view beyond its desired size, but should not contract it. Thus if the scroll win shrinks, the TttView will be scrollable. When an application does scrolling, the view should get the messages. So we set the client window appropriately now. ****************************************************************************/ STATUS PASCAL TttUtilCreateScrollWin( OBJECT clientWin, P OBJECT pScrollWin) SCROLL_WIN_NEW scrollWinNew; STATUS s; ObjCaIIJmp(msgNewDefaults, clsScrollWin, 'scroIIWinNew, s, Error); scroIIWinNew.scroIIWin.clientWin = clientWin; scroIIWinNew.scroIIWin.style.expandChildWidth = true; scroIIWinNew.scroIIWin.style.expandChildHeight = true; ObjCaIIJmp(msgNew, clsScrollWin, ,scroIIWinNew, 5, Error); *pScrollWin = scrolIWinNew.object.uid; return stsOK; Error: return s; } /* TttUtilCreateScrollWin */ 1***************************************************** *********************** TttUtilCreateMenu This is in a utility routine rather than in the caller because including menu.h brings in too many symbols. ****************************************************************************/ STATUS PASCAL TttUtilCreateMenu( parent, OBJECT client, OBJECT P UNKNOWN pEntries, // really P_TK_TABLE_ENTRY pEntries P OBJECT pMenu) MENU NEW mn; STATUS s; ObjCaIIJmp(msgNewDefaults, clsMenu, 'mn, s, Error); mn.win.parent = parent; mn.tkTable.client = client; mn.tkTable.pEntries = (P_TK_TABLE_ENTRY)pEntries; I ObjCaIIJmp(msgNew, clsMenu, 'mn, s, Error); *pMenu = mn.object.uid; return stsOK; Error: return s; } /* TttUtilCreateMenu */ /**************************************************************************** TttUtilAdjustMenu "Adjusts" the menu by removing the items that this app does not support. Each menu that has an item removed from it must be layed out and "break adjusted." The latter involves adjusting the border edges and margins for a menu with lines dividing it into sections. But because the Standard Application Menus can change, we don't want to compile in knowledge of which menus these are. (The menu item's containing menu is easy to find -- it's just the menu item's parent window.) The obvious approach is to simply layout and break adjust the item's menu after removing the item. Unfortunately this potentially results in laying out and break adjusting the same menu several times, since several of the items could be removed from the same menu. Our solution is to keep an array of all unique parents seen. The array is known to be no longer than than the array of disableTags, so space allocation is easy. TttUtilUniqueSort is used to do the unique insertion. Finally, we run over the unique array and do the necessary operations on the menus. ****************************************************************************/ idefine DbgTttUtilAdjustMenu(x) \ TttDbgHelper ("TttUtilAdjustMenu", tttUtilDbgSet,.Ox2, x) /I // Tags which correspond to the menu items that this application // will not implement. II static TAG disableTags[] tagAppMenuSearch, tagAppMenuSpell, tagAppMenuExport}; idefine N_DISABLE_TAGS (SizeOf (disableTags) / SizeOf (disableTags [0]) ) STATUS PASCAL TttUtilAdjustMenu( OBJECT menuBar) WIN parentMenus [N_DISABLE_TAGS]; U16 parentMenuCount; // There are at most // one menu per tag. TIC-TAC-TOE SAMPLE CODE .,., en WIN_METRICS wm; U16 i; OBJECT 0; STATUS s; DbgTttUtilAdjustMenu «""» memset(parentMenus, 0, sizeof(parentMenus»; parentMenuCount = 0; for (i=O; i maxVersion» { DbgTttUtilReadVersion «"version mismatch; v=%ld", (U32) (*pVersion») s = stsIncompatibleVersion; goto Error; DbgTttUtilReadVersion ( ("version=%ld; return stsOK", (U32) (*pVersion) ) ) retllrn stsOK; Error: DbgTttUtilReadVersion(("Error; return Ox%lx",s» return s; } 1* TttUtilReadVersion *1 1***************************************************** *********************** TttUtilGetComponents Note: this is an internal utility routine. Therefore it does not check carefully for null values. ****************************************************** **********************1 STATUS PASCAL TttUtilGetComponents( OBJECT app, getFlags, U16 P OBJECT pScrollWin, pView, P OBJECT P OBJECT pDataObject) view; am; clientWin; s; OBJECT APP METRICS OBJECT STATUS II II II II II Get the scrollWin regardless of the getFlags because we need the scrollWin to get anything else and we assume the getFlags aren't empty. ObjCallJmp(msgAppGetMetrics, app, &am, s, Error); ObjCallJmp(msgFrameGetClientWin, am.mainWin, &clientWin, s, Error); if (FlagOn (tttGetScrollWin, getFlags» ( *pScrollWin = clientWin; II II II Do we need anything else? if (FlagOn (tttGetView, getFlags) OR FlagOn (tttGetDataObject, ,getFlags» II II II II Get the view regardless of the getFlags because we need the view to get either the view or the dataObject. ObjCallJmp(msgScrollWinGetClientWin, clientWin, &view, s, Error); if (FlagOn (tttGetView, getFlags» ! *pView = view; if (FlagOn (tttGetDataObject, getFlags» ! ObjCallJmp(msgViewGetDataObject, view, pDataObject, s, Error); return stsOK; Error: I return s; } 1* TttUtilGetComponents *1 /**************************************************************************** TttUtilPostNotImplementedYet FIXME: In final product, this should not be needed! And if for some reason it is needed, the string(s) in here, and all the strings this is called with, should be in some centralized spot! ****************************************************** **********************1 #define DbgTttUtilPostNIY(x) \ TttDbgHelper("TttUtilPostNotImplementedYet",tttUtilDbgSet,Ox8,x) static U8 formatStr[] = "Sorry. %s is not implemented yet."; #define MAX_FEATURE_STR_LEN 50 #define BUF_SIZE (SizeOf(formatStr) + MAX_FEATURE_STR_LEN + 5) STATUS PASCAL TttUtilPostNotImplementedYet( P_STRING featureStr) noteNew; TK TABLE ENTRY tkEntry[2]; U8 buf[BUF_SIZE]; STATUS s; DbgTttUtilPostNIY (("featureStr=<%s>", featureStr» /! II Initialize for error recovery. /! noteNew.object.uid = objNull; /! II Set up the note string. Check for overflow first. /! if (strlen(featureStr) > MAX_FEATURE_STR_LEN) s stsBadParam; goto Error; NOTE~NEW sprintf(buf,formatStr,featureStr); DbgTttUtilPostNIY ( ("buf=<%s>" ,buf) ) /! II Set up tk table. Tk tables are null terminated, which is why II we have two entries with the second one null. /! memset(&tkEntry[O], 0, sizeof!TK_TABLE_ENTRY»; memset(&tkEntry[l], 0, sizeof(TK_TABLE_ENTRY»; tkEntry[O] .argl = buf; /! II Create and show the note. Because the nfAutoDestroy flag is set, II we don't have to do anything about freeing the resources later. /! ObjCallJmp(msgNewDefaults, clsNote, ¬eNew, s, Error); noteNew.note.metrics.flags = nfAutoDestroy I nfDefaultAppFlags; TlC-TAC-TOE SAMPLE CODE loa ...loa noteNew. note .pContentEntries = tkEntry; ObjCaIIJmp(msgNew, clsNote, ¬eNew, s, Error); ObjCaIIJmp(msgNoteShow, noteNew.object.uid, pNull, s, Error); return stsOK; Error: if (noteNew.object.uid) ObjCaIIWarn(msgDestroy, noteNew.object.uid, pNull); STATUS s; DbgTttUtilTakeSel«""» ObjCaIIJmp(msgSeISetOwner, theSelectionManager, (P_ARGS) object, \ s, Error); StsJmp (InputSetTarget (object, OL), s, Error); II FIXME: These are almost certainly not the right flags. DbgTttUtilTakeSel ( ("return stsOK"» return stsOK; Error: DbgTttUtilTakeSel «"Error; returns Ox%lx", s» return s; } 1* TttUtilTakeSel *1 return s; } 1* TttUtilPostNotImplementedYet *1 1***************************************************** *********************** TttUtilGiveUpSel This utility routine encapsulates the steps need to give up the selection. Whenever this app gives up the selection, it also gives up the focus. '***************************************************** ***********************1 tdefine DbgTttUtilGiveUpSel(x) \ TttDbgHelper ("TttUtilGiveUpSel",tttUtilDbgSet, Ox10,x) STATUS PASCAL TttUtilGiveUpSel( OBJECT object) STATUS 1***************************************************** *********************** TttUtilInitTextOutput ****************************************************** **********************1 void PASCAL TttUtilInitTextOutput( P_SYSDC_TEXT_OUTPUT p, U16 align, P_U8 buf) memset(p, 0, sizeof(*p»; p->spaceChar = (U16)' '; p->stop = maxS32; p->alignChr = align; p->pText = buf; i f (buf) { p->lenText = strlen(buf); s; DbgTttUtilGiveUpSel ( ("") ) ObjCaIIJmp(msgSeISetOwner, theSelectionManager, pNull, \ s, Error); if (object == InputGetTarget(» { StsJrnp(InputSetTarget(objNull, OL), s, Error); II FIXME: Put correct flags in here. } 1* TttUtilInitTextOutput *1 DbgTttUtilGiveUpSel «"return stsOK"» return stsOK; Error: DbgTttUtilGiveUpSel«"Error; returns Ox%lx",s» return s; } 1* TttUtilGiveUpSel *1 mvIEW.H 1***************************************************** *********************** 1***************************************************** *********************** TttUtilTakeSel 'This utility routine encapsulates the steps need to take the selection. Whenever this app takes the selection, it also takes the focus. ****************************************************** **********************1 tdefine DbgTttUtiITakeSel(x) \ TttDbgHelper("TttUtiITa~eSel",tttUtiIDbgSet,Ox20,x) STATUS PASCAL TttUtilTakeSel( OBJECT object) I I I File: tttview.h Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice,) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.3 $ $Date: 04 Dec 1991 13: 39: 14 $This file contains the API definition for clsTttView. clsTttView inherits from clsView. ... • clsTttView displays a representation of clsTttData as a grid of XS and Os. ****************************************************************************1 ,ifndef TTTVIEW_INCLUDED 'define TTTVIEW_INCLUDED ,ifndef CLSMGR_INCLUDED finclude 'endif ,ifndef WIN_INCLUDED 'include 'endif fifndef VIEW_INCLUDED 'include 'endif iifndef SYSGRAF_INCLUDED finclude iendif fifndef TTTPRIV_INCLUDED tinclude 'endif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** TttviewOptions returns STATUS Called in response to msgSelOptions and the "Check" gesture. *1 1* * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II II STATUS PASCAL TttViewOptions( OBJECT PP TTT VIEW INST self, pData) ; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Exported Functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** Tags used by the view's option sheet. ClsTttViewInit returns STATUS Initializes I installs clsTttView. This routine is only called during installation of the class. fdefine tagTttViewOptionSheetMakeTag(clsTttView, 0) tdefinetagTttViewCard MakeTag(clsTttView, 1) tdefine tagCardLineThickness MakeTag(clsTttView, 2) *1 II II typedef struct TTT_VIEW_INST TTT VIEW METRICS metrics; SYSDC dc; II AKN - currentCell is used to hold hit row/col TTT_DATA_SET_SQUARE currentCell; selectedRange; RECT32 TTT_VIEW_INST, * P_TTT_VIEW_INST, * * PP_TTT_VIEW_INST; STATUS PASCAL ClsTttViewInit (void); Tags for TTT'squick help. II fdefine tagTttQHelpForView MakeTag(clsTttView, 3) fdefine tagTttQHelpForLineCtrl MakeTag(clsTttView, 4) II tagCardLineTickness (defined above) is also used for quick help. /* * 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * C01lllllon Typedefs * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 typedef OBJECT TTT_VIEW, * P_TTT_VIEW; typedef struct { U32 lineThickness; U32 sparel; U32 spare2; TTT_VIEW_METRICS, * P_TTT_VIEW_METRICS, TTT_VIEW_NEW_ONLY, * P_TTT_VIEW_NEW_ONLY; I * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Messages for clsTttView * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1**************************************************************************** msgNew takes P_TTT_VIEW_NEW, returns STATUS category: class message Creates an instance of clsTttView. *1 fdefine tttViewNewFields viewNewFields TTT VIEW NEW ONLY \ \ tttView; typedef struct TTT_VIEW_NEW tttViewNewFields TTT_VIEW_NEW, * P_TTT_VIEW_NEW; TlC-TAC-TOE SAMPLE CODE lot lot 10 /**************************************************************************** msgNewDefaults takes. P_TTT_VIEW_NEW, returns STATUS category: class message Initializes the TTT VIEW NEW structure to default values. pArgs->view.createDataObject true; pArgs->tttView.lineThickness 5L; pArgs->tttView.sparel 0; pArgs->tttView.spare2 = 0; */ I I /**************************************************************************** msgTttViewGetMetricstakes P_TTT_VIEW_METRICS, returns STATUS Gets TTT VIEW metrics. */ MakeMsg(clsTttView, 0) 'define msgTttViewGetMetrics /**************************************************************************** msgTttViewSetMetricstakes P_TTT_VIEW_METRICS, returns STATUS Sets the TTT_VIEW metrics. */ ide fine msgTttViewSetMetrics MakeMsg(clsTttView, 1) /**************************************************************************** msgTttViewToggleSel takes nothing, returns STATUS Causes the view to toggle whether or not it holds the selection. */ tdefine msgTttViewToggleSel MakeMsg(clsTttView, 2) /**************************************************************************** msgTttViewTakeSel takes nothing, returns STATUS Causes the view to toggle whether or not it holds the selection. */ Idefine msgTttViewTakeSel MakeMsg(clsTttView, 3) iendif // TTTVIEW_INCLUDED /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * AKN - defines for constants (r,w) * * * * • * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ ide fine Idefine numberOfRows numberOfColumns 5 5 mYlEW.e /**************************************************************************** File: tttview.c Copyright 1990-1992 GO. Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", ,WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED W~TTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1 .6 $ 07 Jan 1992 17:18:56 $ $Date: This file contains the implementation of clsTttView. ****************************************************************************/ iifndef CONTROL_INCLUDED tinclude tendif lifndef RESFILE_INCLUDED linclude lendif lifndef XGESTURE_INCLUDED linclude lendif lifndef PEN_INCLUDED tinclude tendif tifndef STDIO_INCLUDED linclude tendif lifndef SYSGRAF_INCLUDED tinclude tendif lifndef TTTDATA_INCLUDED iinclude lendif lifndef TTTVIEW_INCLUDED iinclude tendif lifndef TTTPRIV_INCLUDED linclude tendif tifndef OSHEAP_INCLUDED tinclude tendif tifndef PREFS_INCLUDED linclude lendif lifndef SEL_INCLUDED finclude lendif lifndef KEY_INCLUDED tinclude tendif tinclude tinclude u o 1**************************************************************************** ,ifndef APPTAG_INCLUDED iinclude 'endif 'ifndef DEBUG_INCLUDED 'include 'endif TttViewFi+edDataOFromInstData Computes filed data from instance data. ****************************************************************************1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 STATIC void PASCAL TttViewFiledDataOFromInstData{ P TTT VIEW INST pInst, P TTT VIEW FILED pFiled) ° pFiled->lineThickness = pInst->metrics.lineThickness; 1* TttViewFiledDataOFromInstData *1 If II II II II II CURRENT_VERSION is the file format version written by this implementation. MIN_VERSION is the minimum file format version readable by this implementation. MAX_VERSION i~ the maximum file format version readable by this implementation. idefine CURRENT_VERSION 0 'define MIN_VERSION 'define MAX_VERSION II II II 1**************************************************************************** TttViewInstDataFromFiledDataO Computes instance data from filed data. ****************************************************************************1 STATIC void PASCAL TttViewInstDataFromFiledDataO{ P TTT_VIEW_FILED_O pFiled, P TTT VIEW INST pInst) °° The desired size for the tic-tac-toe view pInst->metrics.lineThickness = pFiled->lineThickness; 1* TttViewInstDataFromFiledDataO *1 idefine desiredWidth 200 idefine desiredHeight200 typedef struct TTT_VIEW_FILED_O U32 lineThickness; TTT_VIEW_FILED_O, * P_TTT_VIEW_FILED_O; 1**************************************************************************** TttViewNeedRepaint Marks for repaint. ****************************************************************************1 STATIC STATUS PASCAL TttViewNeedRepaint{ OBJECT self) /! II II This struct defines the positions and sizes of all of the interesting pieces of the window. return ObjCallWarn{msgWinDirtyRect, self, pNull); /! II II 1* TttViewNeedRepaint *1 It is a relatively large structure, but since it is only used as a stack variable, its size isn't of much concern. 1**************************************************************************** /! typedef,struct ( U32 normalBoxWidth; U32 normalBoxHeight; RECT32 vertLines[2]; RECT32 horizLines[2]; RECT32 r[3] [3]; scale; SCALE TTT_VIEW_SIZES, TttViewCreateDC Constructs and initializes dc. This routine uses the system font as of the time the dc is created. No effort is made to track changes to the system font. ****************************************************************************1 * P_TTT VIEW SIZES; 1* * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 idefine DbgTttViewCreateDC(x) \ TttDbgHelper("TttViewCreateDC",tttViewDbgSet,Oxl,x) STATIC STATUS PASCAL TttViewCreateDC( OBJECT self, pDC) P OBJECT SYSDC NEW dcNew; nC-TAC-TOE I SAMPLE CODE .. ~ Col ~ Col RES READ DATA PREF_SYSTEMJONT STATUS DbgTttViewCreateDC « II II II resRead; font; pGesture, value) ~ II Either tttX or tttO s; "self=Ox%lx", self)) Initialize for error recovery dcNew.object.uid = objNull; *pDC = objNull; II II II P GWIN GESTURE TTT_SQUARE_VALUE Create the dc ObjCallJmp(msgNewDefaults, clsSysDrwCtx, &dcNew, s, Error); ObjCallJmp(msgNew, clsSysDrwCtx, &dcNew, s, Error); II II Set the dc's font to the current system font. II resRead.resId = prSystemFont; resRead.heap = Nil(OS_HEAP_ID); resRead.pData = &font; resRead.length= SizeOf(font); ObjCallJmp(msgResReadData, theSystemPreferences, &resRead, s, Error); ObjCallJmp(msgDcOpenFont, dcNew.object.uid, &(font.spec), s, Error); TTT_DATA_SET_SQUARE set; OBJECT dataObject; WIN METRICS wm; STATUS s; DbgTttViewGestureSetSquare«"hot=[%ld %ld] ",pGesture->hotPoint)) II II II ObjCallJmp(msgWinGetMetrics, self, &wm, s, Error); if (pGesture->hotPoint.x < (wm.bounds.size.w I 3)) set.col = 0; else if (pGesture->hotPoint.x < (2 * (wm.bounds.size.w I 3))) { set.col 1; else { set.col 2; i f (pGesture->hotPoint.y < (wm.bounds.size.h I 3)) { set.row = 0; else if (pGesture->hotPoint.y < (2 * (wm.bounds.size.h I 3))) { set.row = 1; else ( set.row = 2; II II Bind dc to self II ObjCallJmp(msgDcSetWindow, dcNel~.object.uid, (P_ARGS) self, s, Error); *pDC = dcNew.object.uid; DbgTttViewCreateDC « "return stsOK")) return stsOK; Error: if (dcNew.object.uid) ObjCallWarn (msgDestroy, dcNe!w. object. uid, pNull); Compute row and col II II Set new square value. II DbgTttViewCreateDC «"Error; returns Ox%lx", s)) return s; } 1* TttViewCreateDC *1 set.value = value; ObjCallJmp(msgViewGetDataObject, self, &dataObject, s, Error); ObjCallJmp (msgTttDa·taSetSquare, dataOb.ject, &set, s, Error); DbgTttViewGestureSetSquare «"return stsOK")) return stsOK; Error: DbgTttViewGestureSetSquare «"Error; returns Ox%lx", s)) return s; } 1* TttViewGestureSetSquare *1 1***************************************************** *********************** 1***************************************************** *********************** TttViewGestureSetSquare Handles all gestures that set the value of a single square. ** ** * *** ** * *1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / idefine DbgTttViewGestureSetSquare(x) \ TttDbgHelper("TttViewGestureSetSquare",tttViewDbgSet,Ox2,x) STATIC STATUS PASCAL TttViewGestureSetSquare( self, VIEW TttViewInitAndRestoreCommon Has code common to Init and Restore ****************************************************************************/ idefine DbgTttViewInitAndRestoreCommon(x) \ TttDbgHelper("TttViewInitAndRestoreCommon",tttViewDbgSet,Ox4,x) STATIC STATUS PASCAL TttViewInitAndRestoreCommon( VIEW self, P_TTT_VIEW_INST pInst) set.value STATUS s; DbgTttViewInitAndRestoreCommon (("self=Ox%lx", self)) II II II Initialize for Error Recovery pInst->dc = objNull; II II II Recreate the dc StsJmp(TttViewCreateDC(self, &(pInst->dc)), s, Error); DbgTttViewInitAndRestoreCommon (("return stsOK")) return stsOK; Error; DbgTttViewInitAndRestoreCommon(("Error; returns Ox%lx",s)) return s; } 1* TttViewInitAndRestoreCommon *1 1***************************************************** *********************** TttViewSingleKey Utility routine to handle single key. Actions; * x sets upper left cell to X * Y sets upper left cell to Y * space sets upper left cell to Blank ****************************************************************************1 tdefine DbgTttViewSingleKey(x) \ TttDbgHelper("TttViewSingleKey",tttViewDbgSet,Ox8,x) STATIC STATUS PASCAL TttViewSingleKey( OBJECT self, U16 keyCode) TTT_DATA_SET_SQUARE set; OBJECT dataObject; BOOLEAN input Ignored; STATUS s; DbgTttViewSingleKey ( ("keyCode=%ld=%c" , (U32) keyCode, (U8) keyCode} } ObjCa11Jmp(msgViewGetDataObject, self, &dataObject, s, Error); input Ignored = TRUE; if (.(keyCode == ' x') OR (keyCode == ' X' )) ( input Ignored = FALSE; set.value = tttX; else i f ( (keyCode == ' 0') OR (keyCode ' 0')) ( ,inputIgnored = FALSE; set.value = tttO; .) else if (keyCode == ' ') input Ignored = FALSE; I SAMPLE CODE tttBlank; if (input Ignored) { s = stsInputIgnored; else ( set.row = 2; set.col = 0; ObjCallJmp(msgTttDataSetSquare, dataObject, &set, s, Error); s = stsInputTerminate; DbgTttViewSingleKey ( ("return Ox%lx", (U32) s) ) return s; Error; DbgTttViewSingleKey «"Error; return Ox%lx", s)} return s; } 1* TttViewSingleKey *1 1***************************************************** *********************** TttViewKeyInput Utility routine that handles all clsKey input events. Assumes ancestor is not interested in keyboard input. Note that one and only one of msgKeyMulti and msgKeyChar should be handled. ****************************************************************************/ #define DbgTttViewKeyInput(x) \ TttDbgHelper("TttViewKeyInput",tttViewDbgSet,Ox10,x) STATIC STATUS PASCAL TttViewKeyInput( OBJECT self, P INPUT EVENT pArgs) STATUS s; DbgTttViewKeyInput «"self=Ox%lx", self)} ASSERT((ClsNum(pArgs->devCode) == ClsNum(clsKey)}, \ "KeyInput gets wrong cls"); if (MsgNum(pArgs->devCode) == MsgNum(msgKeyMulti}) U16 i; U16 j; P_KEY_DATA pKeyData = (P_KEY_DATA) (pArgs->eventData); for (i=O; i < pKeyData->repeatCount; i++) ( for (j=O; j < pKeyData->multi[i] .repeatCount; j++) s = TttViewSingleKey(self, pKeyData->multi[i] .keyCode}; if (s < stsOK) { break; if (5 < stsOK) break; TIC-TAC-TOE w w w ... else s = stsInputIgnored; DbgTttViewKeyInput «"return OxUx", s» return s; } 1* TttViewKeyInput *1 1***************************************************** *********************** TttViewPenInput Utility routine that handles all clsPen input events. ****************************************************** **********************1 fdefine DbgTttViewPenInput(x) \ TttDbgHelper("TttViewPenInput",tttViewDbgSet,Ox20,x) STATIC STATUS PASCAL en Input ( lAGE msg, ECT self, EVENT pArgs, CON~ ~EXT ctx, PP TTT VIEW_INST pData) TttDbgHelper("TttViewComputeSizes",tttViewDbgSet,Ox40,x) 'define FONT_SIZE_FUDGE 5 STATIC void PASCAL TttViewComputeSizes( P- TTT- VIEW- SIZES p, PP TTT VIEW INST pData, P_RECT32 pBounds) II window bounds U32 thickness; S16 t; DbgTttViewComputeSizes«"bounds=[%ld %ld %ld %ld]", *pBounds» thickness = Max(1L, (*pData)->metrics.lineThickness); p->normalBoxWidth = Max(1L, (pBounds->size.w - (2 * thickness» I 3); p->normalBoxHeight = Max(1L, (pBounds->size.h - (2 * thickness» I 3); II I I x and width of horiztonal stripes /! p->horizLines[O] .origin.x = p->horizLines[1] .origin.x = 0; p->horizLines[O].size.w = p->horizLines[1] .size.w = MaX(1L, pBounds->size.w); II STA TUS s; DbgTttViewPenInput( ("self=Ox%lx",self» ASSERT«ClsNum(pArgs->devCode) == ClsNum(clsPen», "PenInput gets wrong cls"i); ! if (MsgNum(pArgs->devCode) == MsgNum(msgPenHoldTimeout» ( P_PEN_DATA pPen = (P_PEN_DATA) (pArgs->eventData); ObjCallJmp(msgTttViewTakeSel, self, pNull, s, Error); ObjCallJmp(msgWinUpdate, self, pNull, s, Error); ObjCallJmp«pPen~>taps == 0) ? msgSelBeginMove : msgSelBeginCopy, self, &pArgs->xy, s, Error); s = stsInputTerminate; else ( s = ObjectCallAncestorCtx(ctx); DbgTttViewPenInput «"return Ox%lx", s» return s; Error: DbgTttViewPenInput «"Error; returns Ox%lx", s» return s; Unused(msg);Unused(pData); } 1* TttViewPenInput *1 /**************************************************************************** TttViewComputeSizes ****************************************************** **********************1 fdefine DbgTttViewComputeSizes(x) \ II y and height of vertical stripes II p->vertLines[O].origin.y = p->vertLines[1].origin.y = 0; p->vertLines[O].size.h = p->vertLines[1] .size.h = Max(1L, pBounds->siz.e.h); II II x and width of left column. II p->r[O] [Oi.origin.x = p->r[1] [O].origin.x = p->r[2] [O].origin.x = 0; p->r[O] [O].size.w = p->r[1] [O].size.w = p->r[2] [O].size.w = p->normalBoxWidth; II II x and width of left vertical stripe. /! p->vertLines[O].origin.x = p->r[O] [0] .size.w; p->vertLines[O].size.w = thickness; /! II x and width of middle column. II p->r[O] [1].origin.x = p->r[1] [1].origin.x = p->r[2] [1] .origin.x = p->vertLines[O] .origin.x + p->vertLines[O] .size.w; p->r[O] [1] .size.w = Col· oil p->r[2] p->r[2] p->r[2] p->r[2] p->r[2] p->r[l] [l].size.w = p->r[2] [1] .size.w = p->normalBoxWidth; II II II x and width of right vertical stripe. p->vertLines[l] .origin.x = p->r[O] [1] .origin.x + p->r[O] [1] .size.w; p->vertLines[l] .size.w = thickness; II II II x and width of right column. p->r[O] p->r[l] p->r[2] p->r[O] p->r[l] p->r[2] II II II [O].origin.y [1] .origin.y [2] .origin.y [O].size.h = [1] .size.h = [2] .size.h = = = = 0; p->normaIBoxHeight; y and height of bottom horizontal stripe. p->horizLines[O] .origin.y = p->r[O] [0] .size.h; p->horizLines[O] .size.h = thickness; II II II y and height of middle row. p->r[l] p->r[l] p->r[l] p->r[l] p->r[l] p->r[l] II II II [0] .origin.y [1] .origin.y [2] .origin.y = p->horizLines[O] .origin.y + p->horizLines[O] .size.h; [O}.size.h = [1] .size.h = [2] .size.h = p->normaIBoxHeight; y and height of top horizontal stripe. p->horizLines [l].origin.y = p->r[l] [O].origin.y + p->r[l] [O].size.h; p->horizLines[l] .size.h = thickness; II II II y and height of top row. ~ p->r[2] [0] .origin.y = I Accumulate all extra height here. Compute font scale info. if ((p->normaIBoxWidth - FONT_SIZE_FUDGE) > 0) { t = (S16) (p->normaIBoxWidth - FONT_SIZE_FUDGE); else ( t = 0; [2].origin.x = [2] .origin.x = [2] .origin.x = p->vertLines[l] .origin.x + p->vertLines[l] .size.w; [2] .size.w = [2] .size.w = [2] .size.w = Max(lL, (pBounds->size.w(p->vertLines[O] .size.w + p->vertLines[l] .size.w + p->r[O] [O].size.w + p->r[O] [l].size.w)); p->scale.x = FxMakeFixed(t, 0); if ((p->norma1BoxHeight - FONT_SIZE_FUDGE) > 0) { t = (S16) (p->norma1BoxHeight - FONT_SIZE_FUDGE); else ( t = 0; y and height of bottom row. p->r[O] p->r[O] p->r[O] p->r[O] p->r[O] p->r[O] II II II II II II Accumlate all extra width here. [1] .origin.y = [2] .origin.y = p->horizLines[l] .origin.y + p->horizLines[l] .size.h; [0] .size.h = [1] .size.h = [2] .size.h = Max(lL, (pBounds->size.h(p->horizLines[O] .size.h + p->horizLines[l] .size.h + p->r[O] [O].size.h + p->r[l] [O].size.h))); p->scale.y = FxMakeFixed(t, 0); DbgTttViewComputeSizes(("nBW=%ld nBH=%ld",p->normaIBoxWidth, p->normaIBoxHeight)) DbgTttViewComputeSizes (("vert [%ld %ld %ld %ld] [%ld %ld %ld %ld]", p->vertLines[O] , p->vertLines[l])); DbgTttViewComputeSizes(("horiz [%ld %ld %ld %ld] [%ld %ld %ld %ld]", p->horizLines[O], p->hor"izLines[l])); DbgTttViewComputeSizes(( "rO [%ld %ld %ld %ld] [%ld %ld %ld %ld] [%ld %ld %ld %ld]", p->r [0] [0], p->r [0] [1], p->r [0] [2])) ; DbgTttViewComputeSizes(( "r1 [%ld %ld %ld %ld] [%ld %ld %ld %ld] [%ld%ld %ld %ld]", p->r[l] [0], p->r[l] [1], p->r[l] [2])); DbgTttViewComputeSizes(( "r2 [%ld %ld %ld %ld] [%ld %ld %ld %ld] [%ld %ld %ld %ld]", p->r [2] [0], p->r [2] [1], p->r [2] [2] )); } 1* TttViewComputeSizes *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * *. * * * * * * * * * * *1 1***************************************************** *********************** TttViewNewDefaults Respond to msgNewDefaults. ****************************************************************************1 #define DbgTttViewNewDefaults(x) \ TttDbgHelper("TttViewNewDefaults",tttViewDbgSet,Ox80,x) TlC-TAC-TOE lit Col VI SAMPLE CODE lot Col MsgHandlerWithTypes(TttViewNewDefaults, P_TTT_VIEW_NEW, ObjCallJmp(msgNewDefaults, clsTttData, &tttDataNew, s, Error); ObjCallJmp(msgNew, clsTttData, &tttDataNew, s, Error); responsibleForDataObject = true; pArgs->view.createDataObject = false; pArgs->view.dataObject = tttDataNew.object.uid; PP~TTT_VIEW_INST) { DbgTttViewNewDefaults «tlself=Ox%lx tl , self» pArgs->win.flags.input 1= inputHClldTimeout; pArgs->gWin. helpId = tagTttQHelpE'orView; pArgs->embeddedWin.style.moveable = true; pArgs->embeddedWin.style.copyable = true; pArgs->view.createDataObject true; pArgs->tttView.lineThickness = 5L; pArgs->tttView.sparel = 0; pArgs->tttView.spare2 = 0; DbgTttViewNewDefaults «"return stsOK » return stsOK; MsgHandlerParametersNoWarning; } 1* TttViewNewDefaults *1 tI /*************************************.~************** ************************ TttViewInit Initialize instance data of new object. Note: clsmgr has already initialized instance data to zeros. ****************************************************** **********************1 idefine DbgTttViewInit(x) \ TttDbgHelper(tlTttViewInit",tttViewDbgSet,OxlOO,x) MsgHandlerWithTypes(TttViewInit, P_TTT_VIEW_NEW, PP_TTT_VIEW_INST) } 1/ II II 1/ ObjCallAncestorCtxJmp(ctx, s, Error); responsibleForDataObject = false; 1/ II Use the utility routine to handle things common to Init and Restore. 1/ StsJmp (TttViewInitAndRestoreCommon (self, pInst), s, Error); ObjectWrite(self, ctx, &pInst); DbgTttViewInit «tlreturn stsOK » return stsOK; MsgHandlerParametersNoWarning; Error: if (responsibleForDataObject AND tttDataNew.object.uid) { ObjCallWarn(msgDestroy, tttDataNew.object.uid, pNull); II clsView will notice that the data object has been destroyed II and will update its instance data, so no need to ObjectWrite. tI { P_TTT_VIEW_INST pInst; TTT DATA_NEW tttDataNew; STATUS s; BOOLEAN responsibleForDataObject; DbgTttViewInit «tlself=Ox%lx", self» II II Initialize for error recovery II pInst = pNull; tttDataNew.object.uid = objNull; responsibleForDataObject = false; II II Allocate instance data and initialize those parts of it II that come from pArgs. II StsJmp(OSHeapBlockAlloc(osProcessHeapId, SizeOf(*pInst), &pInst), \ s, Error); pInst->metrics.lineThickness = pArgs->tttView.lineThickness; /I /I Create the data object, i f appropriate. II if «pArgs->view.dataObject == Nil(OBJECT» AND (pArgs->view.createDataObject» { Now let ancestor finish initializing self. clsView will make self an observer of the data object. i f (pInst) { OSHeapBlockFree(pInst); DbgTttViewInit«"Error; returns Ox%lx",s» return s; . } 1* TttViewInit *1 /**************************************************************************** TttViewFree Respond to msgFree. Note: Always return stsOK, even if a problem occurs. This is (1) because there's nothing useful to do if a problem occurs anyhow and (2) because the ancestor is called after this function if and only if stsOK is returned, and it's important that the ancestor get called. ****************************************************************************/ 'define DbgTttViewFree(x) \ TttDbgHelper( tl TttViewFree",tttViewDbgSet,Ox200,x) MsgHandlerWithTypes(TttViewFree, P_ARGS, PP_TTT_VIEW_INST) { OBJECT dataObject; DbgTttViewFree «tlself=Ox%lx", self» (1;\ if ({*pData)->dc).{ ObjCallWarn{msgDestroy, (*pData)->dc, pNull); if (ObjCallWarn{msgViewGetDataObject, self, &dataObject) >= stsOK) { if (dataObject) { ObjCallWarn{msgViewSetDataObject, self, objNull); ObjCallWarn{msgDestroy, dataObject, pNull); J OSHeapBlockFree{*pData); DbgTttViewFree{ ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; /* TttViewFree */ /**************************************************************************** TttViewSave Save self to a file. ****************************************************************************/ idefine DbgTttViewSave{x) \ . TttDbgHelper (ITttViewSave", tttViewDbgSet, Ox400, x) MsgHandlerWithTypes{TttViewSave, P_OBJ_SAVE, PP_TTT_VIEW_INST) { TTT- VIEW- FILED- 0 filed; STATUS s; DbgTttViewSave {(" self=Ox%lx" , self) ) StsJmp{TttUtilWriteVersion{pArgs->file, CURRENT VERSION), s, Error); TttViewFiledDataOFromInstData{*pData, &filed); StsJmp {TttUtilWrite {pArgs->file, SizeOf{filed), &filed), s, Error); DbgTttViewSave{ ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewSave ({"Error; return Ox%lx", s» return S; ) /* TttViewSave */ /**************************************************************************** TttViewRestore Restore self from a file. Note: clsmgr has already initialized instance data to zeros. ****************************************************************************/ idefine DbgTttViewRestore{x) \ TttDbgHelper{ITttViewRestore",tttViewDbgSet,Ox800,x) MsgHandlerWithTypes{TttViewRestore, P_OBJ_RESTORE, PP_TTT_VIEW_INST) { P- TTT- VIEW- INST I pInst; filed; TTT- VIEW- FILED- 0 version; TTT VERSION STATUS S; DbgTttViewRestore ({lself=Ox.%lx", self) ) /I .// Initialize for error recovery. /I pInst = pNull; /I // Read version, then read filed data. (Currently there's only // only one legitimate file format, so no checking of the version // need be done.) /I // The allocate instance data and convert filed data. /I StsJmp{TttUtilReadVersion{pArgs->file, MIN_VERSION, MAX_VERSION, \ &version), s, Error); StsJmp{TttUtilRead{pArgs->file, SizeOf{filed), 'filed), s, Error); StsJmp{OSHeapBlockAlloc{osProcessHeapId, SizeOf{*pInst), &pInst), \ s, Error); TttViewInstDataFromFiledDataO{&filed, pInst); /I // Use the utility routine to handle things common to Init and Restore. /I StsJmp{TttViewInitAndRestoreCommon{self, pInst), s, Error); ObjectWrite{self, ctx, &pInst); DbgTttViewRestore { (" return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: i f (pInst) { OSHeapBlockFree{pInst); DbgTttViewRestore ({"Error; return Ox%lx", s» return S; ) /* TttViewRestore */ /**************************************************************************** TttViewDump Respond to msgDump. ****************************************************************************/ Ufdef DEBUG MsgHandlerWithTypes{TttViewDump, P_ARGS, PP_TTT_VIEW_INST) { Debugf {"TttViewDump: dc=Ox%lx lineThickness=%ld", {*pData)->dc, (U32) ({*pData)->metrics.lineThickness»; return stsOK; MsgHandlerParametersNoWarning; TIC-TAC-TOE SAMPLE CODE a.a ... Col ., • W } 1* TttViewDump *1 lendif II idefine DbgTttViewRepaint{x) \ TttDbgHelper {ITttViewRepaint",tttViewDbgSet, Ox2000,x) MsgHandlerWithTypes{TttViewRepaint, P_ARGS, PP_TTT_VIEW_INST) DEBUG I***********************************,t**************** ************************ TttViewDataChanged Respond to changes in viewed object. ************************************,~**************** ***********************1 idefine DbgTttViewDataChanged{x) \ TttDbgHelper{ITttViewDataChanged",tttViewDbgSet,Ox1000,x) MsgHandlerWithTypes{TttViewDataChanged, P_TTT_DATA_CHANGED, PP_TTT_VIEW_INST) { STATUS s; DbgTttViewDataChanged { (" self=Ox%lx pArgs=Ox%lx", self, pArgs) ) if (pArgs == pNull) ( ObjCaIIJmp{msgWinDirtyRect, self, pNull, s, Error); else ( WIN METRICS . wm; TTT VIEW SIZES sizes; DbgTttViewDataChanged{{"row=%ld col=%ld", (U32) (pArgs->row), \ (U32) (pArgs->col» ) ObjCaIIJmp{msgWinGetMetrics, (*pData)->dc, &wm, s, Error); TttViewComputeSizes{&sizes, pData, &(wm.bounds»; ObjCaIIJmp{msgWinDirtyRect, {*pData)->dc, \ &(sizes.r[pArgs->r01~] [pArgs->col]), s, Error); DbgTttViewDataChanged{ ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewDataChanged { ("Error; l=eturn Ox%lx", s) ) return s; } 1* TttViewDataChanged *1 TTT_VIEW_SIZES sizes; SYSDC TEXT OUTPUT tx; XY32 sizeX; XY32 sizeO; OBJECT dataObject; BOOLEAN endRepaintNeeded; WIN METRICS wm; RECT32 dirtyRect; TTT DATA METRICS dm; U16 row; U16 col; i-; U16 OBJECT owner; STATUS s; BOOLEAN drawIt; DbgTttViewRepaint ({lself=Ox%lx", self» II II General initialization and intialization for error recovery. II Also collect miscellaneous info needed to paint. II endRepaintNeeded = false; ObjCaIIJmp{msgViewGetDataObject, self, &dataObject, s, Error); ObjCaIIJmp{msgTttDataGetMetrics, dataObject, &dm, s, Error); ObjCaIIJmp{msgWinGetMetrics, (*pData)->dc, &wm, s, Error); TttViewComputeSizes{&sizes, pData, &(wm.bounds»; II II Must do msgWinBeginRepaint before any painting starts, and II to get dirtyRect. II 1***************************************************** *********************** TttViewRepaint Respond to msgRepaint. This handler demonstrates how to do smart repainting. It asks the window manager for the "dirty" rectangle, and then only repaints those objects that intersect with the rectangle. Smart repainting should be used by applications that have expensive repaintirig procedures. Although Tic-Tac-Toe's repainting is not even close to being prohibitive, it uses smart repainting for the sake of demonstrating how to do it. Instead of using smart repainting, many applications simply redraw their entire window. For an example of this approach, take a look at Hello World (Custom Window) . ****************************************************** **********************1 II { II ObjCaIIJmp{msgWinBeginRepaint, (*pData)->d~, &dirtyRect, s, Error); endRepaintNeeded = true; II ImagePoint ROUNDS from LWC to LUC, but DrawRectangle TRUNCATES. II Therefore, increase th~ size of the dittyRect so as to be sure to II cover all the pixels that need to be painted. II Another solution would be to use finer LUC than points. dirtyRect.origin.x--; dirtyRect.origin.y--; dirtyRect.size.w += 2; dirtyRect.size.h += 2; II II Fill the dirty rect with the appropriate background. If we hold the II selection, the appropriate background is grey, otherwise it is white. II ObjCaIIJmp{msgSeIOwner, theSelectionManager, &owner, s, Error); if (owner == self) { DbgTttViewRepaint ({"owner is self"» ObjectCall(msgDcSetBackgroundRGB, (*pData)->dc, \ (P_ARGS)sysDcRGBGray33); else { DbgTttViewRepaint «"owner is not self"» ObjectCall(msgDcSetBackgroundRGB, (*pData)->dc, \ (P_ARGS)sysDcRGBWhite); ObjectCall(msgDcSetFiIIPat, (*pData)->dc, (P ARGS)sysDcPatBackground); ObjectCall(msgDcSetLineThickness, (*pData)->dc, (P_ARGS)OL); ObjectCall{msgDcDrawRectangle, (*pData)->dc, &dirtyRect); ObjectCall(msgDcSetFiIIPat, (*pData)->dc, (P_ARGS)sysDcPatForeground); II II Paint the vertical lines /I for (i=O; i<2; i++) { if (Rect32sIntersect(&dirtyRect, &(sizes.vertLines[i]») { DbgTttViewRepaint «"vertical i=%ld; overlap", (U32) i» ObjectCall(msgDcDrawRectangle, (*pData)->dc, \ &(sizes.vertLines[i]»; else { DbgTttViewRepaint«"vertical i=%ld; no overlap", (U32)i» } /I II Paint the horizontal lines /I for (i=O; i<2; i++) { if (Rect32sIntersect(&dirtyRect, &(sizes.horizLines[i]») { DbgTttViewRepaint«"horizontal i=%ld; overlap", (U32)i» ObjectCall(msgDcDrawRectangle, (*pData)->dc, \ &(sizes.horizLines[i]»; else { DbgTttViewRepaint «"horizontal i=%ld; no overlap", (U32) i» DbgTttViewRepaint «"measure X=[%ld %ld]", sizeX.x, sizeX.y» TttUtillnitTextOutput(&tx, sysDcAlignChrTop, pNull); tx.pText = "0"; tx.lenText = strlen(tx.pText); ObjectCall(msgDcMeasureText, (*pData)->dc, (P ARGS)&tx); sizeO = tx.cp; DbgTttViewRepaint ( ("measure 0= [%ld %ld]", sizeO. x, sizeO. y) ) /I II Paint the cells. /I for (row=O; row<3; row++) for (col=O; col<3; col++) i f (Rect32sIntersect (&dil:tyRect, &. (sizes. r [row] [col]) » DbgTttViewRepaint«"row=%ld col=%ld; overlap", \ (U32) row, (U32) col»; if (dm.squares[row] [col] == tttX) ( drawIt = TRUE; tx.pText = "X"; tx.lenText = strlen(tx.pText); tx.cp.x = sizes.r[row] [col].origin.x + «sizes.r[row] [col] .size.w - sizeX.x) I 2); tx.cp.y = sizes. r [row] [col].origin.y + sizeX.y + «sizes.r[row] [col] .size.h - sizeX.y) ./ 2); else if (dm.squares[row] [col] == tttO) ( drawlt = TRUE; tx.pText = "0"; tx.lenText = strlen(tx.pText); tx.cp.x = sizes.r[row] [col] .origin.x + «sizes.r[row] [col].size,w - sizeO.x) I 2); tx.cp.y = sizes. r [row] [col] .origin.y + sizeO.y + «sizes.r[row] [col].size.h - sizeO.y) I 2); else { DbgTttViewRepaint ( ("blank cell"» drawIt = FALSE; } /I II Scale the font to. the box size. /I II Note: This. could be done once when the window size II changes rather than each time the window is painted. /I ObjCaIIJmp(msgDcldentityFont, (*pData)->dc, pNull, s, Error); ObjCaIIJmp(msgDcScaleFont, (*pData)->dc, &(sizes.scale), s, Error); /I II Measure X and 0 in the·font. /I TttUtillnitTextOutput(&tx, sysDcAlignChrTop, pNull); tx.pText = "X"; tx.lenText = strlen(tx.pText); ObjectCall(msgDcMeasureText, (*pData)->dc,· (P_ARGS)&tx); sizeX = tx.cp; I i f (drawlt) ( ObjCaIIJmp(msgDcDrawText, (*pData)->dc, &tx, s, Error); else DbgTttViewRepaint«"row=%ld col=%ld; no overlap", \ (U32) row, (U32)col»; /I II Balance the msgWinBeginRepaint /I ObjCa~IWarn(msgWinEndRepaint, (*pData)->dc, pNull); DbgTttViewRepaint ( ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; TIC-TAC-TOE ., " 00 SAMPLE CODE Error: if (endRepaintNeeded) { ObjCaIIWarn(msgWinEndRepaint, (*pData)->dc, pNull); DbgTttViewRepaint «"Error; return Ox%lx", s» return s; } 1* TttViewRepaint *1 1***************************************************** *********************** TttViewGetDesiredSize Respond to msgGetDesiredSize. The desired size is an appropriate minimum size for the drawing. ****************************************************** **********************1 'define DbgTttViewGetDesiredSize (x). \ TttDbgHelper("TttViewGetDesiredSize",tttViewDbgSet,Ox2000,x) MsgHandlerArgType(TttViewGetDesiredSi2:e, P_WIN_METRICS) { pArgs->bounds.size.w = desiredWidth; pArgs->bounds.size.h = desiredHeight; DbgTttViewGetDesiredSize ( ("return stsOK")} return stsOK; MsgHandlerParametersNoWarning; } 1* TttViewGetDesiredSize *1 1***************************************************** *********************** TttViewGesture Let ancestor handle unrecognized gestures. ****************************************************** **********************1 'define DbgTttViewGesture(x) \ TttDbgHelper("TttViewGesture",tttViewDbgSet,Ox8000,x) MsgHandlerWithTypes(TttViewGesture, P_GWIN_GESTURE, PP_TTT_VIEW_INST) case MsgNum(xgsCross) : StsJmp (TttViewGestureSetSquare (self, pArgs, tttX), \ s, Error); break; case MsgNum(xgsCircle.): StSJmp(TttViewGestureSetSquare(self, pArgs, tttO), \ s, Error); break; case MsgNum(xgsPigtaiIVert): case MsgNum(xgspigtaiIHorz) : StsJmp(TttViewGestureSetSquare(self, pArgs, tttBlank), \ s, Error); break; case MsgNum(xgsCheck) : II Make sure there is a selection. ObjCaIIJmp(msgSeIOwner, theSelectionManager, \ &owner, s, Error); if (owner != self) ( ObjCaIIJmp(msgTttViewTakeSel, self, pNull, s, Error); ObjCaIIJmp(msgWinUpdate, self, pNull, s, Error); } II Then call the ancestor. ObjCallAncestorCtxJmp(ctx, s, Error); break; default: DbgTttViewGesture«"Letting ancestor handle gesture"» return ObjCallAncestorCtxWarn(ctx); break; default: DbgTttViewGesture«"Letting ancestor handle gesture"» return ObjCaIIAncestorCtxWarn(ctx); { STATUS OBJECT tifdef DEBUG s; owner; { P_CLS_SYMBUF mb; DbgTttViewGesture «"self=Ox%lx msg=Ox%lx %s", self, pArgs->msg, CIsMsgToString(pArgs->msg,mb») . DbgTttViewGesture «"return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewGesture«"Error; return Ox%lx",s» return s; } 1* TttViewGesture *1 } fendif I I DEBUG switch(CIsNUm(pArgs->msg» case CIsNum(clsXGesture): switch(MsgNum(pArgs->msg» case MsgNum(xgslTap): ObjCaIIJrnp(msgTttViewToggleSel, self, pNull, \ s, Error); break; 1***************************************************** *********************** TttViewSelYield msgSelYield from selection manager. ****************************************************** **********************1 'define DbgTttViewSeIYield(x) \ TttDbgHelper("TttViewSeIYield",tttViewDbgSet,OxlOOOO,x) MsgHandlerWithTypes(TttViewSeIYield, P_ARGS, PP_TTT_VIEW_INST) ~ o s; STATUS DbgTttViewSeUield( ("self=Ox%lx", self)) StsJmp(TttViewNeedRepaint(self), s, Error); DbgTttViewSelYield( ("return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewSelYield( ("Error; return Ox%lx", sll return s; } 1* TttViewSelYield *1 1***************************************************** *********************** TttViewSelDelete In this particular application, deleting is a poorly defined concept. Rather than do nothing, though, we clear the board. ****************************************************** **********************1 fdefine DbgTttViewSelDelete(x) \ TttDbgHelper ("TttViewSelDelete",tttViewDbgSet, Ox80000, x) MsgHandlerWithTypes(TttViewSelDelete, P_ARGS, PP_TTT_VIEW_INST) ( TTT DATA METRICS elm; OBJECT dataObject; row; U16 U16 col; s; STATUS DbgTttViewSelDelete«"")) ObjCallJmp(msgViewGetDataObject, self, &dataObject, s, Error); ObjCallJmp(msgTttDataGetMetrics, dataObject, &elm, s, Error); for (row=O; row<3; row++) ( for (col=O; col<3; col++) { elm. squares [row] [col] = tttBlank; } elm.undoTag = tagTttDataUndoDelete; ObjCallJmp(msgTttDataSetMetrics, dataObject, &elm, s, Error); DbgTttViewSelDelete «"returns stsOK")) return'stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewSelDelete ( ("Error; return Ox%lx", s) ) return s; } 1* TttViewSelDelete *1 MsgHandlerWithTypes(TttViewGetMetrics, P_TTT_VIEW_METRICS, PP_TTT_VIEW_INST) ( DbgTttViewGetMetrics«"self=Ox%lx",self)) *pArgs = (*pData)->metrics; DbgTttViewGetMetrics «"returns stsOK")) return stsOK; MsgHandlerParametersNoWarning; } 1* TttViewGetMetrics *1 1***************************************************** *********************** TttViewSetMetrics ****~************************************************* **********************1 'define DbgTttViewSetMetrics(x) \ TttDbgHelper("TttViewSetMetrics",tttViewDbgSet,Ox20000O,x) MsgHandlerWithTypes(TttViewSetMetrics, P_TTT_VIEW_METRICS, PP_TTT_VIEW_INST) { DbgTttViewSetMetrics «"self=Ox%lx", self)) (*pData)->metrics = *pArgs; TttViewNeedRepaint(self); DbgTttViewSetMetrics «"returns stsOK")) return stsOK; MsgHandlerParametersNoWarning; } 1* TttViewSetMetrics *1 1***************************************************** *********************** TttViewToggleSel msgTttViewToggleSel ****************************************************** **********************1 'define DbgTttViewToggleSel(x) \ TttDbgHelper("TttViewToggleSel",tttViewDbgSet,Ox400000,x) MsgHandlerWithTypes(TttViewToggleSel, P_ARGS, PP_TTT_VIEW_INST) ( owner; OBJECT STATUS s; DbgTttViewToggleSel«"self=Ox%lx",self)) ObjCallJmp(msgSelOwner, theSelectionManager, &owner, s, Error); if (owner == self) ( DbgTttViewToggleSel«"View is the owner; set to be objNull")) StsJmp(TttUtilGiveUpSel(self), s, Error); else { DbgTttViewToggleSel ( ("View is not the owner; set to be self")) StsJmp(TttUtilTakeSel(self), s, Error); } 1***************************************************** *********************** TttViewGetMetrics ******************~***~******************************* **********************1 idefine DbgTttViewGetMetrics(x) \ TttDbgHelper("TttViewGetMetrics",tttViewDbgSet,OxlOOOOO,x) , StsJmp(TttViewNeedRepaint(self), s, Error); DbgTttViewToggleSel«"return stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: nC-TAC-TOE I ..AMPI F rnnF ..... ~ .. ~ DbgTttViewToggleSel «"Error; return Ox%lx", s» return s; } /* TttViewToggleSel */ /**************************************************************************** TttViewTakeSel ****************************************************************************/ idefine DbgTttViewTakeSel(x} \ TttDbgHelper("TttViewTakeSel",tttViewDbgSet,Ox800000,x} MsgHandlerWithTypes(TttViewTakeSel, P__ARGS, PP_TTT_VIEW_INST) { OBJECT owner; STATUS s; DbgTttViewTakeSel( ("self=Ox%lx", self» ObjCallJmp(msgSelOwner, theSelectionManager, 'owner, s, Error); if (owner != self) { DbgTttViewTakeSel «"owner is not self; taking"» StsJmp(TttUtilTakeSel(self), s, Error); StsJmp(TttViewNeedRepaint(self), s, Error); else { DbgTttViewTakeSel ( ("owner is already self; doing nothing"» DbgTttViewTakeSel«"returns stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewTakeSel «"Error; return Ox%lx", s» return s; I /* TttViewTakeSel */ ~ s = ObjectCallAncestorCtx(ctx); break; return s; MsgHandlerParametersNoWarning; I /* TttViewInputEvent */ /**************************************************************************** TttViewProvideEnable Respond to msgControlProvideEnable. ****************************************************************************/ MsgHandlerWithTypes(TttViewProvideEnable, P_CONTROL_PROVIDE_ENABLE, PP_TTT_VIEW_INST) ( switch (pArgs->tag) case (tagAppMenuMove): case (tagAppMenuCopy): pArgs->enable = true; break; default: return ObjectCallAncestorCtx(ctx); return stsOK; MsgHandlerParametersNoWarning; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Installation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**************************************************************t************* ClsTttViewInit /**************************************************************************** TttViewInputEvent msgInputEvent. ****************************************************************************/ 'define DbgTttViewInputEvent(x) \ TttDbgHelper("TttViewInputEvent",tttViewDbgSet,OxlOOOOOO,x) MsgHandlerWithTypes(TttViewInputEvent, P_INPUT_EVENT, PP_TTT_VIEW_INST) ( STATUS s; switch (ClsNum(pArgs->devCode» case ClsNum(clsPen): s = TttViewPenInput(msg, self, pArgs, ctx, pData); break; case ClsNum(clsKey): s = TttViewKeyInput(self, pArgs); break; default: Install the class. ****************************************************************************/ STATUS PASCAL CIsTttViewInit (void) ( CLASS_NEW new; STATUS s; ObjCallJmp(msgNewDefaults, clsClass, 'new, s, Error); new.object.uid clsTttView; new. object. key 0; new.cls.pMsg clsTttViewTable; new. cIs. ancestor clsView; new .cls. size SizeOf (P_TTT_VIEW_INST); new.cls.newArgsSize SizeOf(TTT_VIEW_NEW); ObjCallJmp(msgNew, clsClass, 'new, s, Error); return stsOK; Error: return s; } 1* ClsTttViewInit *1 iendif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * TnVOPT.C File: tttvopt. c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may ~se this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ $Date: 04 Dec 1991 13:39:18 $ This file contains the implementation of c1sTttView's Option Sheets. Notes: * STATIC TK_TABLE_ENTRY cardEntries[] = { {"Line Thickness:", 0, 0, 0, 0, 0, tagTttQHelpForLineCtrl}, {"1", 0, 0, tagCardLineThickness, 0, clsIntegerField, tagTttQHelpForLineCtrl}, {pNull} }; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 [1] The Option Sheet protocol allows any class of an object to create an option sheet and/or add cards. Therefore this code carefully validates that it only operates on Option Sheets and Cards that it knows about. This could be overkill. 1**************************************************************************** TttViewOptionAddCards Handles msgOptionAddCards. ****************************************************************************1 I ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 II II The following static maps to the TK_TABLE_ENTRY struct, in tktable.h II It is short-hand for defining a TkTable. II 1**************************************************************************** iifndef OPTION_INCLUDED iinclude iendif iifndef TTTVIEW_INCLUDED iinclude iendif iifndef DEBUG_INCLUDED iinclude iendif iifndef OPTTABLE_INCLUDED iinclude iendif iifndef TTTDATA_INCLUDED iinclude iendif iifndef OS_INCLUDED hnclude iendif iifndef SEL_INCLUDED hnclude * Defines, Types, Globals, Etc * I I I Note on error handling: Once a card has been added to the sheet, destroying the sheet will destroy the card. ****************************************************************************1 idefine DbgTttViewOptionAddCards(x) \ TttDbgHelper("TttViewOptionAddCards",tttViewOptsDbgSet,Ox4,x} MsgHandlerWithTypes(TttViewOptionAddCards, P_OPTION_TAG, PP TTT VIEW_INST) { OPTION CARD card; STATUS s; DbgTttViewOptionAddCards ( ("") ) II II Create the card. II card. tag = tagTttViewCard; card. win = objNull; card. pName = "TTT Card"; card. client = self; ObjCallJmp(msgOptionAddLastCard, pArgs->option, &card, s, Error); DbgTttViewOptionAddCards ( ("return stsOK")) return stsOK; TIC-TAC-TOE SAMPLE CODE ~ olio Col MsgHandlerParametersNoWarning; Error: DbgTttViewOptionAddCards«"Error; return Ox%lx",s» return s; ) 1* TttViewOptionAddCards *1 1***************************************************** *********************** TttViewOptionProvideCard Handles msgOptionProvideCardWin. ****************************************************** **********************1 fdefine DbgTttViewOptionProvideCard(x) \ TttDbgHelper (ITttViewOptionProvideCard", tttViewOptsDbgSet, Ox8,'x) MsgHandlerWithTypes(TttViewOptionProvideCard, P_OPTION_CARD, P_UNKN9WN) { STATUS s; OPT ION_TABLE_NEW otn; DbgTttViewOptionProvideCard«"I» pArgs->win = objNull; if (pArgs->tag == tagTttViewCard) { ObjCaIIJmp(msgNewDefaults, clsOptionTable, &otn, s, Error); otn.tkTable.client = self; otn.tkTable.pEntries = cardEntries; otn.win.tag = pArgs->tag; otn.gWin.helpId = tagCardLineThickness; ObjCaIIJmp(msgNew, clsOptionTable, &otn, s, Error); pArgs->win = otn.object.uid; return (stsOK) ; MsgHandlerParametersNoWarning; Error: DbgTttVieWOptionProvideCard ( ("Error; return Ox%lx", s) ) return s; } 1* TttViewOptionProvideCard *1 1***************************************************** *********************** TttViewOptionRefreshCard Handles msgOptionRefreshCard ****************************************************** **********************1 'define DbgTttViewOptionRefreshCard(x) \ TttDbgHelper(ITttViewOptionRefreshCard",tttViewOptsDbgSet,Ox10,x) MsgHandlerWithTypes(TttViewOptionRefreshCard,'P_OPTION_CARD, PP_TTT_VIEW_INST) ( view; OBJECT vm; TTT- VIEW METRICS control; OBJECT STATUS s; DbgTttViewOptionRefreshCard«"I» ... II II See note [11 at the beginning of this file. iii oil oil II if (pArgs->tag != tagTttViewCard) { DbgTttViewOptionRefreshCard«"unrecognized card; call ancestor"» return ObjCallAncestorCtxWarn(ctx); } II II II Collect info needed to refresh card. StsJmp(TttUtiIGetComponents(OSThisApp(), tttGetView, \ objNull, &view, objNull), s, Error); ObjCaIIJmp(msgTttViewGetMetrics, view, &vm, s, Error); DbgTttViewOptionRefreshCard( ("refreshing card"» control = (OBJECT) ObjectCall(msgWirtFindTag, pArgs->win, \ (P_ARGS)tagCardLineThickness); ObjCallJmp(msgControlSetValue, control, (P_ARGS) (vm.lineThickness), \ s, Error); II II II The whole card is clean now. ObjCaIIJmp(msgControISetDirty, pArgs->win, (P_ARGS) false, s, Error); DbgTttViewOptionRefreshCard( ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewOptionRefreshCard( ("Error; return Ox%lx", s» return s; } 1* TttViewOptionRefreshCard *1 1***************************************************** *********************** TttViewOptionApplyCard Handles msgOptionApplyCard Note: Perhaps this should be an undoable operation. ****************************************************** **********************1 'define DbgTttViewOptionApplyCard(x) \ TttDbgHelper("TttViewOptionApplyCard",tttViewOptsDbgSet,Ox20,x) MsgHandlerWithTypes(TttViewOptionApplyCard, P_OPTION_CARD, PP_TTT_VIEW_INST) { OBJECT view; vm; TTT VIEW METRICS BOOLEAN dirty; OBJECT control; U32 value; OBJECT owner; s; STATUS DbgTttViewOptionApplyCard( (""» II II II See note [1] at the beginning of this file. if (pArgs->tag != tagTttViewCard) { DbgTttViewOptionRefreshCard«"unrecognized card; call ancestor"» return ObjCallAncestorCtxWarn(ctx); II0bjCaIIAncestorCtxWarn(); it is not an error for the ancestor to II return stsFailed, and we don't want to generate a debugging message. II if (pArgs->tag != tagTttViewCard) ( DbgTttViewOptionApplicableCard«"unrecognized card; call ancestor"» return ObjectCallAncestorctx(ctx); } II II Collect info needed to apply card. II StsJmp(TttUtiIGetComponents(OSThisApp(), tttGetView, objNull, \ &view, objNull), s, Error); DbgTttViewOptionApplyCard( ("applying card"» control = (OBJECT) ObjectCall(msgWinFindTag, pArgs->win, \ II II SO it's a ttt card. Decide if it's consistent with the current seln. II ObjCa1IJmp(msgSeIOwner, theSelectionManager, &owner, s, Error); if (owner == self) ( DbgTttViewOptionApplicableCard«"owner is self; return stsOK"» return stsOK; else ( DbgTttViewOptionApplicableCard«"owner is not self; return stsFailed"» return stsFailed; (P~GS)tagCardLineThickness); ObjCaIIJmp(msgControIGetDirty, control, &dirty, s, Error); if (dirty) ( II Promote the view's selection, if it is,not already promoted. ObjCaIIJmp(msgSeIOwner, theSelectionManager, &owner, s, Error); if (owner != self) ( ObjCallJmp(msgSeISetOWnerPreserve, theSelectionManager, \ pNull, s, Error); ObjCaIIJmp(msgControlGetValue, control, &value, s, Error); DbgTttViewQptionApplyCard «" \ "Line Thickness \" is dirty; value=%ld", val ObjCaIIJmp(msgTttViewGetMetrics, view, &vm, s, Error); vm.lineThickness = value; ObjCaIIJmp(msgTttViewSetMetrics, view, &vm, s, Error); else { DbgTttViewOptionApplyCard( ("\"Line Thickness\" is not dirty"» DbgTttViewQptionApplyCard( ("return stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewOptionApplyCard( ("Error; return Ox%lx", s» return s; I 1* TttViewQptionApplyCard *1 1**************************************************************************** TttViewOptionApplicableCard Handles mSgQptionApplicableCard ****************************************************************************1 'define DbgTttViewOptionApplicableCard(x} \ TttDbgHelper ("TttViewOptionApplicableCard", tttViewQptsDbgSet ,Ox80,x) MsgHandlerWithTypes(TttViewOptionApplicableCard, P_OPTION_CARD, \ PP_TTT_VIEW_INST) OBJECT owner; s; STATUS DbgTttViewOptionApplicableCard( (.... » II II See note [I] at the beginning of this file. Also, don't use MsgHandlerParametersNoWarning; Error: » DbgTttViewOptionApplicableCard«"Error; return Ox%lx",s» return Si 1* TttViewOpt~onApplicableCard *1 mYXFER.C 1**************************************************************************** File: tttvxfer.c Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.1 $ $Date: 02 Dec 1991 19:08:02 $ This file contains the implementation of clsTttView's Data Transfer ****************************************************************************1 'ifndef TTTVIEW_INCLUDED 'include 'endif ,ifndef LIST_INCLUDED 'include fendif nC-TAC-TOE I ~ II SAMPLE CODE ...lot iifndef XFER_INCLUDED iinclude iendif iifndef SEL_INCLUDED iinclude iendif iifndef TTTDATA_INCLUDED iinclude iendif iifndef EMBEDWIN_INCLUDED iinclude iendif iifndef DEBUG_INCLUDED iinclude iendif 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Utility Routines * * * * * * * * * * * * * * * * * * * * * * .* * * * * * * * * * * * * * * * *1 * 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Message Handlers * * * * * * * * * * * * • * * * * * * * * * * * * * * * * * * * * * * ** * * *1 1***************************************************** *********************** TttViewSelBeginMoveAndCopy Handles both msgSelBeginMove and msgSelBeginCopy ****************************************************** ******~***************I tdefine DbgTttViewSeIBeginMoveAndCopy(x) \ TttDbgHelper ("TttViewSeIBeginMove~AndCopy" , tttViewXferDbgSet, Ox1, x) MsgHandlerWithTypes(TttViewSelBeginMoveAndCopy, P_XY32, PP_TTT_VIEW_INST) { EMBEDDED- WIN- BEGIN- MOVE- COPY bmc; STATUS s; DbgTttViewSelBeginMoveAndCopy ( (" s:elf=Ox%lx" , self) ) i f (pArgs) { bmc.xy = *pArgs; else { bmc.xy.x bmc.xy.y = 0; bmc.bounds.origin.x bmc.bounds.origin.y bmc.bounds.size.w = bmc.bounds.size.h = 0; ObjCaIIJmp(MsgEqual(msg, msgSelBeginMove) ? msgEmbeddedWinBeginMove : msgEmbeddedWinBeginCopy, self, &bmc, s, Error); DbgTttViewSelBeginMoveAndCopy ( (" returns stsOK")) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttViewSelBeginMoveAndCopy ( ("Error; return Ox%lx", s) ) return s; } 1* TttViewSelBeginMoveAndCopy *1 1***************************************************** *********************** TttViewXferGet ****************************************************** **********************1 idefine DbgTttViewXferGet(x) \ TttDbgHelper("TttViewXferGet",tttViewXferDbgSet,Ox2,x) MsgHandlerWithTypes(TttViewXferGet, P_XFER_FIXED_BUF, PP_TTT_VIEW_INST) { STATUS s; DbgTttViewXferGet «"self=Ox%lx", self)) if (pArgs->id == xferString) OBJECT dataObj; TTT_DATA_METRICS dm; U16 row; U16 col; P_XFER_FIXED_BUF p = (P_XFERJIXED_BUF)pArgs; ObjCallJmp(msgViewGetDataObject, self, &dataObj, s, Error); ObjCallJmp(msgTttDataGetMetrics, dataObj, &dm, s, Error); II II initialize the length to the number of squares (9) plus 1 II to allow for a string termination character (just in case II the user copies/moves the string into a text processor. II p->len = 10; p->data = OL; for (row=O; row<3; row++) { for (col=O; col<3; col++) p->buf[(row*3)+col] = dm. squares [row] [col]; , \0' ; p->buf[9] s = stsOK; else { s = ObjectCaIIAncestorCtx(ctx); DbgTttViewXferGet «"returns Ox%lx", s)) return s; MsgHandlerParametersNoWarning; Error: DbgTttViewXferGet «"Error; return Ox%lx", s)) 01 return s; ] /* TttViewXferGet */ /**************************************************************************** TttViewXferList ****************************************************************************/ static TAG sourceFormats [] = {xferString]; idefine N_SOURCE_FORMATS (SizeOf(sourceFormats) / SizeOf(sourceFormats[O]) idefine DbgTttViewXferList(x) \ TttDbgHelper("TttViewXferList",tttViewXferDbgSet,Ox4,x) MsgHandlerWithTypes(TttViewXferList, OBJECT, PP TTT VIEW INST) - { -, - STATUS s; DbgTttViewXferList «"self=Ox%lx", self» /I // Don't let ancestor add types. We aren't interested in // moving/copying the window, which is the only type the // ancestor supports. /I StsJmp(XferAddIds(pArgs, sourceFormats, N_SOURCE_FORMATS), s, Error); DbgTttViewXferList«"returns stsOK"]) return stsOK; MsgHandlerParametersNoWarning; Error: DbgTttviewXferList ( ("Error; return Ox%lx", s) ) return s; ] /* TttViewXferList */ /******************~********************************************************* TttViewSelMoveAndSelCopy Handles both msgSelMoveSelection and msgSelCopySelection ****************************************************************************/ static TAG receiverFormats[] = {xferString}; idefine N_RECEIVER_FORMATS (SizeOf(receiverFormats) / SizeOf(receiverFormats[O]» idefine DbgTttViewSelMoveAndSelCopy(x) \ TttDbgHelper("TttViewSelMoveAndSelCopy",tttViewXferDbgSet,Ox8,x) MsgHandlerWithTypes(TttViewSelMoveAndSelCopy, P_XY32, PP_TTT_VIEW_INST) { TAG transferType; OBJECT owner; XFER LIST NEW listNew; s; STATUS DbgTttViewSelMoveAndSelCopy ( (" self=Ox%lx" , self) ) /I // Initialize for error recovery I /I listNew.object.uid = NULL; /I // Get source of move/copy. /I ObjCallJmp(msgSelOwner, theSelectionManager, &owner, s, Error); i f (! owner) { DbgTttViewSelMoveAndSelCopy«"no owner!"» s = stsFailed; goto Error; /I // // // /I if Don't bother doing move/copy to self. Use the Error exit out of this routine even though this really isn't really an error. FIXME: Inform the user or not? Wait for UI Guidelines. (owner == self) { DbgTttViewSelMoveAndSelCopy( ("owner s = stsOK; goto Error; == self"» /I // Get list of available types. /I ObjCallJmp(msgNewDefaults, clsXferList, &listNew, s, Error); ObjCallJmp(msgNew, clsXferList, ~listNew, s, Error); ObjCallJmp(msgXferList, owner, listNew.object.uid, s, Error); StsJmp (XferListSearch (listNew.object.uid, receiverFormats, N_RECEIVER_FORMATS, &transferType), s, Error); /I // This only handles one transfer type now, but we expect to handle // more in the future. So code it in that style. /I if (transferType == xferString) TTT_DATA_METRICS metrics; OBJECT dataObj; XFER_FIXED_BUF xfer; U16 i; DbgTttViewSelMoveAndSelCopy«"transferType is xferString"» ObjCallJmp (msgViewGetDataObject, self, &dataObj, s, .Error); ObjCallJmp(msgTttDataGetMetrics, dataObj, &metrics, s, Error}; xfer;id = xferString; ObjSendUpdateJmp(msgXferGet, owner, &xfer, SizeOf(xfer), s, Error); DbgTttViewSelMoveAndSelCopy «"data=%ld len=%ld", (U32) (xfer. data), (U32) (xfer . len» ) for (i=O; i < (U16}Min(xfer.len,9L); i++) { metrics.squares[i/3] [i%3] = TttUt.ilSquareValueForChar (xfer .buf [i] ) ; lIC-TAC-TOE . SAMPLE CODE .~ .... metrics.undoTag = tagTttDataUndoMoveCopy; ObjCallJmp(msgTttDataSetMetrics, dataObj, &metrics, s, Error); else { gotoError; II II II If this was a move, delete the source. if (MsgEqual(msgSelMoveSelection, msg» { ObjSendU32Jmp(msgSelDelete, owner, (P_ARGS)SelDeleteNoSelect, s, Error); II II II II II Take the selection. Be sure to do this AFTER deleting the selection because the source may "forget" what to delete when the selection is pulled from it. ObjCallJmp(msgTttViewTakeSel, self, pNull, s, Error); ObjCallWarn(msgDestroy, listNew.object.uid, pNull); DbgTttViewSelMoveAndSelCopy ( (" returns stsOK"» return stsOK; MsgHandlerParametersNoWarning; Error: if (listNew.object.uid) { ObjCallWarn(msgDestroy, listNew.object.uid, pNull); DbgTttViewSelMoveAndSelCopy ( ("Error; return Ox\lx", s» return s; I 1* TttViewSelMoveAndSelCopy *1 Resource Files mMISC.RC 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * File: tttmisc.rc Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 tifndef RESCMPLR_INCLUDED tinclude tendif #ifndef TTTPRIV_INCLUDED itinclude "tttpriv.h" #endif static P_STRING tttTKStrings[] = { "Undo Delete", I I 0 -, special undo menu string for deletes "Undo Move/Copy", II 1 - special undo menu string for moves & copies pNull I; static RC_INPUT tttTKResStrings = { MakeListResId(clsTttData, resGrpTK, 0), tttTKStrings, sizeof(tttTKStrings), resStringArrayResAgent I; P RC INPUT resInput [] = { &tttTKResStrings, pNull I; mQHELP.RC 1**************************************************************************** File: tttqhelp.rc Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND' GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. Tic-Tac-Toe Quick Help resources. ****************************************************************************1 #ifndef RESCMPLR_INCLUDED iinclude #endif itifndef QHELP~INCLUDED iinclude fendif iifndef TTTVIEW_INCLUDED Unclude "tttview.h" fendif II II II Quick Help string for ttt's option card. .•... static CHAR tttOptionString[] = ( II Title for the quick help window "TTT Cardll" II Quick help text "Use this option card to change the thickness of the lines " "on the Tic-Tac-Toe board." Ii /! II Quick Help string for the line thickness control in ttt's option card. /! static CHAR tttLineThicknessString[] = ( II Title for the quick help window "Line Thickness I I" II Quick help text "Change the line thickness by writing in a number from 1-9." Ii II II II Quick Help string for the view. static CHAR tttViewString[] = ( II Title for the quick help window "Tic-Tac-Toel I" II Quick help text "The Tic-Tac-Toe window lets you to make X's and 0'5 in a Tic-Tac-Toe " "grid. You can write X's and O's and make move, copy" "and pigtail delete gestures.\n\n" "It does not recognize a completed game, either tied or won.\n\n" "To clear the game and start again, tap Select All in the Edit menu, " "then tap Delete." Ii II Define the quick help resource for the view. static P_RC_TAGGED_STRINGtttViewQHelpStrings[] = tagCardLineThickness, tttOptionString, tagTttQHelpForLineCtrl, tttLineThicknessString, tagTttQHelpForView, tttViewString, pNul1 Ii static RC_INPUT tttViewHelp = MakeListResld(clsTttView, resGrpQhelp, 0), tttViewQHelpStrings, II Name of the string array 0, resTaggedStringArrayResAgent II Use string array resource agent Ii 1**************************************************************************** The glue that ties everything together -- reslnput. ****************************************************************************1 II II II reslnput is an exported variable that the resource compiler expects. Each element is a pointer to a structure describing the next resource. The list is terminated with a null pointer. i? RC INPUT reslnput [] = ( I &tttViewHelp, II this is the one defined in this example any other resource pointers would go here pNull II Ii MAKEFILE iiiiiiiiiiiiiiiiiiiii•• iiiiiiiiiiiiiiiiiiiiiiii i i i i i i i i i i i i i i i i i i WMake Makefile for TTT (Tic-tac-Toe) Copyright 1990-1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.10 $ $Date: 07 Jan 1992 17:18;40 $ iii.iii •• iiiiiiiiiiiiiii.iiiiiii.iiiiiiiiiiiiii i Set PENPOINT PATH to your environment variable, if it exists. i Otherwise, set it to \penpoint !ifdef %PENPOINT PATH PENPOINT_PATH = $(%PENPOINT_PATH) !else PENPOINT_PATH = \penpoint !endif MODE = debug i The DOS name of your project directory. PROJ = ttt i Standard defines for sample code !INCLUDE $(PENPOINT_PATHI\sdk\sample\sdefines.mif i The PenPoint name of your application EXE NAME = Tic-Tac-Toe i The linker name for your executable: company-name-V ( 1 EXE_LNAME= GO-TIC_TAC_TOE-V1 (1) i The app's version (if undefined, srules .mif define,S it to be 1. 0) APP_VERSION = 1.1 i Object files needed to build your app EXE_OBJS = methods.obj & tttapp.obj & tttdata.obj & TIC-TAC-TOE SAMPLE CODE .. •o ~ VI tttview.obj & tttvopt.obj & tttdbg.obj & tttmbar. obj & tttutil.obj & tttvxfer.obj & s_ttt.obj it Libs needed to build your app EXE_LIBS = penpoint app win input xfer xtemplt it The .res files for your project; t1~ically these will be bitmap resources it and/or compiled resource compiler files. If you have resources, add it $(APP_DIR)\app.res to the "all" target. . RES_FILES = 19icon.res smicon.res tttqhelp.res tttmisc.res It Targets all: $ (APP_DIR)\$ (PROJ) .exe $(APP_DIR)\app.res .SYMBOLIC it Install the help files. Note that you should copy the help files # in the reverse order from how you want them to appear in the help it notebook. help :: . SYMBOLIC mkdir $(PENPOINT_PATH)\app\ttt\help mkdir $(PENPOINT PATH)\app\ttt\help\ttthelp2 copy strat.txt $(PENPOINT PATH)\app\ttt\help\ttthelp2\help.txt mkdir $ (PENPOINT PATH)\app\ttt\help\ttthelpl copy rules.txt $(PENPOINT_PATH)\app\ttt\help\ttthelpl\help.txt -$ (STAMP) $(PENPOINT PATH)\app\ttt\help /g "Tic-Tac-Toe Rules" /d ttthelpl -$ (STAMP) $(PENPOINT=PATH)\app\ttt\help /g "Tic-Tac-Toe Strategy" /d ttthelp2 # Install the stationery files. Each stationery file must be in a separate it directory off of statnry. Here, we also stamp the stationery directories it wi th meaningful names. Note that the "filled" stationery directory is also it stamped with attributes so that it appears in the "Create" menu. stationery:: .SYMBOLIC mkdir $(PENPOINT_PATH)\app\ttt\statnry mkdir $(PENPOINT_PATH)\app\ttt\statnry\tttstatl copy filled. txt $(PENPOINT_PATH)\app\ttt\statnry\tttstatl\tttstuff.txt mkdir $ (PENPOINT PATH)\app\ttt\statnry\tttstat2 copy xsonly.txt $(PENPOINT_PATH)\app\ttt\statnry\tttstat2\tttstuff.txt -$ (STAMP) $(PENPOINT]ATH)\app\ttt\statnry /g "Tic-Tac-Toe (filled)" /d tttstatl /a 00800274 1 -$ (STAMP) $(PENPOINT_PATH)\app\ttt\statnry /g "Tic-Tac-Toe (X's)" /d tttstat2 it The clean rule must be :: because it is also defined in srules clean :: .SYMBOLIC -del methods .,h -del $(APP_DIR)\help\ttthelpl\*.* -del $(APP_DIR)\help\ttthelp2\*.* -del $(APP_DIR)\help\*.* -rmdir $(APP_DIR)\help\ttthelpl -rmdir $ (APP_DIR)\help\ttthelp2 -rmdir $ (APP_DIR)\help -del $(APP_DIR)\statnry\tttstatl\*.* -del $(APP_DIR)\statnry\tttstat2\*.* -del $(APP_DIR)\statnry\*.* -rmdir $(APP_DIR)\statnry\tttstatl -rmdir $(APP_DIR)\statnry\tttstat2 -rmdir $(APP_DIR)\statnry it Dependencies methods.obj: methods.tbl tttpriv.h tttdata.h tttview.h tttapp.h tttapp.obj: methods.h tttapp.c tttview.h tttapp.h tttdata.h tttpriv.h tttdata.obj: methods.h tttdata.c tttdata.h tttpriv.h tttview.obj: methods.h tttview.c tttdata.h tttview.h tttpriv.h tttvopt.obj: tttvopt.c tttview.h tttdbg.obj: tttdbg.c tttpriv.h tttdata.h tttmbar.obj: tttmbar.c tttpriv.h tttapp.h tttvxfer.obj: tttvxfer.c tttview.h tttdata.h # Standard rules for sample code !INCLUDE $(PENPOINT]ATH)\sdk\sample\srules.mif Stationery Source Files XSONLY.TXT x x x x x stationery for tttapp FILLED.TXT xoxoxoxox stationery for tttapp I Help Text Source Files RULES.TXT Tic-Tac-Toe is a simple game for two players. The players take turns writing X's and O's in the grid. The player that gets three X's or three O's in a row (across, down, or diagonally) wins. STRAT.TXT The first player should put her X (or 0) in the center square. By doing so, she increases the possibility of getting three X's (or O's) in a row. o Template Application The template application provides a template for a full-featured application. Obieclives Template Application serves as a shell of an application and can be used as a starting point for a real application. This sample application also shows how to: • File instance data • Create the standard menu bar and add applicationspecific menus • Create an icon window as a client window. Class 'Overview Template Application defines two classes: dsTemplateApp and dsFoo. It makes use of the following classes: dsApp dsAppMgr dsClass dslconWin dsMenu dsObject COlli piling To compile TemplateApp, just cd \penpoint\sdk\sample\ternpltap wrnake This compiles the application and creates TEMPLTAP.EXE andAPP.RES in \PENPOINT\APP\TEMPLTAP. Running As its name implies, Template Application is a template, "cookie cutter" application. As such, it does not exhibit much functionality. However, it does handle many "typical" application messages. This aspect makes Template Application a good starting point for building a real application. After compiling TemplateApp, you can run it by 1 Adding \\BOOT\PENPOINT\APP\TemplateApplication to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Creating a new Template Application document, and turning to it. TEMPlATE APPUCATION I SAMPLE CODE .... VI lit III {msgAppCreateClientWiri, "TemplateAppCreateClientWin", {msgAppCreateMenuBar, "TemplateAppCreateMenuBar", {msgTemplateAppGetMetrics, "TemplateAppGetMetrics" , Alternatively, you can boot PeilPoint and then install Template Application through the Connections Notebook. Files Used I; }, {OJ I; MSG_INFO clsFooMethods[] "FooNewDefaults" , {msgNewDefaults, {msgDump, "FooDump", {msgInit, "FooNew", "FooFree", {msgFree, {msgSave, "FooSave", {msgRestore, "FooRestore" , {msgFooGetStyle, "FooGetStyle", {msgFooSetStyle, "FooSetStyle", {msgFooGetMetrics, "FooGetMetrics", . The code for T emplateApplication is in . \PENPOINT\SDK\SAMPLE\TEMPLTAP. The files are: FOO.C the source code for the fooclass FOO.H the header file for the foo class METHODS.TBL the list of messages that the classes respond to; and the associated message handlers to call TEMPIATE.RG resource file containing error message strings - TEMPLTAP.C the source code for the application class I t METHODS.TBl > , 1********************;,********************-************;,**************.******** File: methods.tbl .. Copyright 1991, 1992 GO Corporation. All Rights Reserved . .You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright-notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.0 $ $Date: 07 Jan 1992 16:57:42 $ This file contains the method table definitions for templtap.exe. objCallAncestorBefore objCallAncestorBefore objCallAncestorAfter objCallAncestorBefore objCallAncestorBefore objCallAncestorBefore objCallAncestorAfter objCallAncestorBefore }, ), }, I, I, I, I, I, 'I, }; CLASS_INFO classInfo[} = {"clsTemplateAppTable" , {"clsFooTable", 'clsTemplateAppMethods clsFooMethods I, I, (Ol J; TEMPlTAP.H I*******************K**************************!******************K********** ************************************K***************************************1 'include 'include 'include MSG_INFO clsTemplateAppMethods[] {msgDump, "TemplateAppDump" , "TemplateAppNew" , (msgInit, {msgFree, "TemplateAppFree" , {msgSave, "TemplateAppSave", {msgRestore, "TemplateAppRestore", "TemplateAppInit", {msgApplnit, {msgAppOpen, "TemplateAppOpen" '. {msgAppClose, "TemplateAppClose", objCallAncestorBefore objCallAncestorBefore objCallAncestorBefore objCallAncestorAfter objCallAncestorBefore objCallAncestorBefore {OJ TEMPLTAP.H t~e header file fortlle application class. '. }, I, I, I, I, I, I, I, }, File: templtap.h Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice {including the above copyright_notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY. AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ 13 Nov 1991 18:19:46 $ $Date: This file contains the templateapp application API. ****************************************************************************1 tifndef TEMPLATEAPP INCLUDED 'define TEMPLATEAPP INCLUDED tinclude tdefine clsTemplateApp II Status codes. tdefine stsTernplateAppErrorl tdefine stsTernplateAppError2 MakeWKN(3513, 1, wknGlobal) MakeStatus(clsTemplateApp, 1) MakeStatus(clsTemplateApp, 2) lit II Quick Help codes. MakeTag(clsTemplateApp, 1) *define qhTemplateAppQuickHelp1 *define qhTemplateAppQuickHelp2 MakeTag(clsTemplateApp, 2) typedef OBJECT TEMPLATEAPP, *P_TEMPLATEAPP; typedef struct TEMPLATEAPP_METRICS U32 dummy; U32 reserved; TEMPLATEAPP_METRICS, *P_TEMPLATEAPP_METRICS; 1***************************************************** *********************** msgTemplateAppGetMetricstakes P_TEMPLATEAPP_METRICS, returns STATUS Get TemplateApp metrics. *1 *defihe msgTemp1ateAppGetMetrics *endif II TEMPLATEAPP INCLUDED MakeMsg(clsTemplateApp, 1) static char *version = "1.0"; static char *defaultDocName "Template Application"; typedef struct TEMPLATEAPP_INST { TEMPLATEAPP_METRICS metrics; TEMPLATEAPP_INST, *P_TEMPLATEAPP_INST; typedef struct FILED_DATA ( TEMPLATEAPP_METRICS metrics; FILED_DATA; *P_FILED_DATA; 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * TemplateAppDurnp Respond to msgDurnp. 1***************************************************** *********************** File: templtap.c Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ 07 Jan 1992 16:57:28 $ $Date: This file contains the templtap application. ****************************************************** **********************1 *include *include *include *include *include *include *include *include *include *include * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * static static Rights Message Handlers * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 /**************************************************************************** TEMPLTAP.C /* * Defines, Types, Globals, Etc * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 char *company "GO Corporation"; char *copyright "Copyright \213 1991, 1992\nby GO Corporation,\nAll Reserved."; ****************************************************** **********************1 MsgHandlerArgType(TemplateAppDurnp, P_ARGS) ( Debugf("templtap: msgDurnp"); return stsOK; MsgHandlerParametersNoWarning; II TemplateAppDurnp 1***************************************************** *********************** TemplateAppNew Respond to msglnit. ********************************~********************* **********************/ MsgHandlerArgType(TemplateAppNew, P_APP_NEW) ( return stsOK; MsgHandlerParametersNoWarning; II TemplateAppNew 1***************************************************** *********************** TemplateAppFree Respond to msgFree; ****************************************************** **********************1 MsgHandlerArgType(TemplateAppFree, P_ARGS) { return stsOK; MsgHandlerParametersNoWarning; II TemplateAppFree 1***************************************************** *********************** TemplateAppSave Respond to msgSave. TEMPlATE APPLICATION I ~ VI Col SAMPLE CODE .. tilt VI ****************************************************************************1 MsgHandlerArgType(TemplateAppSave, P_OBJ_SAVE) { P- TEMPLATEAPP- INST pInst; STREAM READ WRITE fsWrite; FILED DATA filed; STATUS s; pInst = IDataPtr (pData,TEMPLATEAPP_ INST) ; memset(&filed, 0, sizeof(FILED_DATA)); filed.metrics = pInst->metrics; II Write filed data to the file. fsWrite.numBytes = SizeOf(FILED_DATA); fsWrite.pBuf = &filed; ObjCallRet(msgStreamWrite, pArgs->file, &fsWrite, s); return stsOK; MsgHandlerParametersNoWarning; II TemplateAppSave 1**************************************************************************** TemplateAppRestore Respond to msgRestore. ****************************************************************************1 MsgHandlerArgType(TemplateAppRestore, P_OBJ_RESTORE) ( fsRead; STREAM READ WRITE TEMPLATEAPP INST inst; filed; FILED DATA STATUS s; memset(&inst, 0, sizeof(TEMPLATEAPP_INST)); II Read instance data from the file. fsRead.numBytes = SizeOf(FILED_DATA); fsRead.pBuf = &filed; ObjCallRet(msgStreamRead, pArgs->file, &fsRead, s); inst.metrics = filed.metrics; II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; II TemplateAppRestore 1***************************************************** *********************** TemplateAppInit Respond to msgAppInit. Perform one-time initializations. ****************************************************************************1 MsgHandlerArgType(TemplateAppInit, DIR_HANDLE) ( APP_METRICS OBJECT am; win; STATUS s; II Create the client win. win = objNull; ObjCallRet(msgAppCreateClientWin, self, &win, s); II Get the main window. ObjCallRet(msgAppGetMetrics, self, &am, s); I I Set the client win. ObjCallRet(msgFrameSetClientWin, am.mainWin, (P_ARGS) win, s); return stsOK; MsgHandlerParametersNoWarning; II TemplateAppInitApp 1**************************************************************************** TemplateAppOpen Respond to msgAppOpen. ****************************************************************************1 MsgHandlerArgType(TemplateAppOpen, P_APP_OPEN) ( FRAME METRICS fm; APP METRICS am; OBJECT menuBar; STATUS s; II Create the menu bar. menuBar = objNull; ObjCallRet(msgAppCreateMenuBar, self, &menuBar, s); II Get the main window. ObjCallRet(msgAppGetMetrics, self, &am, s); II Insert the menu bar. ObjCallRet(msgFrameSetMenuBar, am.mainWin, (P_ARGS)menuBar, s); II Set the childAppParentWin. ObjCallRet(msgFrameGetMetrics, am.mainWin, &fm, s); pArgs->childAppParentWin = fm.clientWin; return stsOK; MsgHandlerParametersNoWarning; II TemplateAppOpen 1************·**************************************************************** TemplateAppClose Respond to msgAppClose. ****************************************************************************/ MsgHandlerArgType(TemplateAppClose, P_ARGS) ( APP METRICS am; STATUS s; ObjCallRet(msgAppGetMetrics, self, &am, s); ObjCallRet(msgFrameDestroyMenuBar, am.mainWin, pNull·, s); return stsOK; MsgHandlerParametersNoWarning; II TemplateAppClose ObjCaIIAncestorRet(msg, self, pArgs, ctx, s); II Fixup the menu bar here ... return stsOK; MsgHandlerParametersNoWarning; II TemplateAppCreateMenuBar 1***************************************************** *********************** TemplateAppCreateClientWin . Respond to msgAppCreateClientWin. ****************************************************** **********************1 MsgHandlerArgType(TemplateAppCreateClientWin, P_OBJECT) 1***************************************************** *********************** TemplateAppGetMetrics { ICON_WIN_NEW iwn; II If the client win has already beed provided, return. if (*pArgs != objNull) { return stsOK; II Create an iconwin. ObjectCall(msgNewDefaults, clsIconWin, &iwn); iwn,iconWin.style.showOptions = true; iwn.iconWin.style.allowOpenInPlace = true; iwn.iconWin.style.constrainedLayout = false; ObjCallWarn(msgNew, clsIconWin, &iwn); II Return.the client win. *pArgs = iwn.object.uid; return stsOK; MsgHandlerParametersNoWarning; II TemplateAppCreateClientWin Get TemplateApp metrics. ****************************************************************************1 MsgHandlerArgType(TemplateAppGetMetrics, P_TEMPLATEAPP_METRICS) { P_TEMPLATEAPP_INST pInst; pInst = IDataPtr (pData, TEMPLATEAPP_INST); *pArgs = pInst->metrics; return stsOK; MsgHandlerParametersNoWarning; II TemplateAppGetMetrics 1* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * . Installation * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1 j*******~********************************************* *********************** ClsTemplateAppInit 1***************************************************** *********************** TemplateAppCreateMenuBar Respond to msgAppCreateMenuBar. *****************************************~************ **********************1 MsgHandlerArgType(TemplateAppCreateMenuBar, P_OBJECT) { STATUS S; lIMENU_NEW mn; II If a sub-class provided the menu bar ... if (*pArgs != objNull) { II Pass message to ancestor - Add S~s. return ObjectCallAncestor(msg, self, pArgs, ctx); } II Create your menu bar here ... IIObjectCall(msgNewDefaults, clsMenu, &mn);. Ilmn.win.flags.style &= -wsSendFile; Ilmn.tkTable.pEntries = menuBar; lImn. tkTable. client = self; IIObjCallRet(msgNew, clsMenu, &mn, s); II*pArgs = mn.object.uid; II Pass message to ancestor - Add S~s. Install the application. ****************************************************** **********************1 STATUS PASCAL ClsTemplateAppInit (void) { APP_MGR_NEW new; STATUS S; ObjectCall(msgNewDefaults, clsAppMgr, &new); new.object.uid clsTemplateApp; = 0; new.object.key new.cls.pMsg = clsTemplateAppTable; new.cls~ancestor = clsApp; new.cls.size SizeOf(TEMPLATEAPP_INST); new.cls.newArgsSize = SizeOf(APP_NEW); new.appMgr.copyright = copyright; RectInit(&new.appMgr.defaultRect, 0, 0, 216, 108); strcpy(new.appMgr.defaultDocName, defaultDocName); strcpy (new.appMgr. company, company);· strcpy(new.appMgr.version, version); ObjCallRet(msgNew, clsAPpMgr, &new, s); return stsOK; STATUS ClsFooInit (void); TEMPlATE APPL£CAnoN I SAMPLE CODE . UI UI lot VI 1***************************************************** *********************** main Main application entry point. ****************************************************** **********************1 void CDECL main ( int argc, char *argv[], int processCount) if (processCount == 0) II Create the application class. StsWarn(ClsTemplateAppInit ()); II Create global classes. StsWarn (ClsFooInit () ) ; II Start msg dispatching. AppMonitorMain(clsTemplateApp, objNull); idefine clsFoo II Status codes. idefine stsFooError1 ide fine stsFooError2 II Quick Help codes. idefine qhFooQuickHelp1 idefine qhFooQuickHelp2 typedef struct FOO_STYLE U16 style1 :1; U16 style2 :1; U16 reserved :14; FOO_STYLE, *P_FOO_STYLE; typedef struct FOO_METRICS FOO STYLE style; U32 reservedl[2]; U16 reserved2 FOO_METRICS, *PJOO_METRICS; MakeWKN(3514,1,wknGlobal) MakeStatus(clsFoo, 1) MakeStatus(clsFoo, 2) MakeTag(clsFoo, 1) MakeTag(clsFoo, 2) :16; II II Reserved. Reserved. 1***************************************************** *********************** else ( II Start msg dispatching. AppMainO; Unused(argc); Unused(argv); II main FOO.K 1***************************************************** *********************** File: foo.h Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ 13 Nov 1991 18:19:48 $ $Date: This file contains the API definition for clsFoo. ****************************************************** **********************1 iifndef FOO_INCLUDED #define FOO_INCLUDED iinclude typedef OBJECT FOO, *P_FOO; msgNew takes P_FOO_NEW, returns STATUS Create a new object. *1 typedef struct FOO_NEW_ONLY I I Your new parameters here ... FOO STYLE style; U32 reserved; FOO_NEW_ONLY, *P_FOO_NEW_ONLY; idefine fooNewFields \ objectNewFields \ FOO_NEW ONLY foo; typedef struct FOO_NEW fooNewFields } FOO_NEW, *P_FOO_NEW; 1***************************************************** *********************** msgFooGetMetricstakes P_FOO_METRICS, returns STATUS Get foo metrics. *1 idefine msgFooGetMetrics MakeMsg(clsFoo, 1) 1***************************************************** *********************** msgFooGetStyle Get foo style. takes P_FOO_STYLE, retutbs STATUS *1 idefine msgFooGetStyle MakeMsg(clsFoO, 2) 1***************************************************** *********************** msgFooSetStyle Set foo style. takes P_FOO_STYLE, returns STATUS *1 idefine msgFooSetStyle MakeMsg(clsFoo, 3) 01 iendif // Foo_INCLUDED FOO.C /**************************************************************************** File: foo.c Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND'FITNESS FOR A PARTICULAR PURPOSE . IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL, INCIDENTAL, OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.2 $ 07 Jan 1992 16:57:22 $ $Date: This file contains the class definition and methods for clsFoo. ****************************************************************************/ iinclude ltinclude , 'include 'include iinclude /* * * * * * * * * * * * ** * * * *,* * * * * *,* * * * * * * * * * * * * * Defines, Types, Globals, Etc * * * * * ** * * * * ~~ ~ * * * * * * * * * * * * * ~ * * * * * * * * * * * *1 typedef struct FOO_INST { Foo-.:METRICS metrics; } 'FOO.:..INST, *P_FOO_INST;, typedef struct FILED..PATA 1/ Your filed instance data he,re ... FOO_STYLE style; U32 reserved1; U16 reserved2 :16; // Reserved. FILE'D_DATA, *p_FILED_DATA; /* * ** * * * * **,* * * * * * * * * * * * * * * * * * * * * * * * * * * * Message' Handlers * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**************************************************************************** FooNewDefaults Respond to msgNewDefatilts. ***********************************~****************************************/ MsgHandlerArgType{FooNewDefaults, P_FOO_NEW) ( memset(&{pArgs->foo), 0, sizeof{FOO_NEW_ONLY»; pArgs->foo. style. style1 = false; false; pArgs->foo. style. style2 pArgs->foo.reserved 0; return stsOK; MsgHandlerParametersNoWarning; 1/ FooNewDefaults /**************************************************************************** FooDump Respond to msgDump. ****************************************************************************/ MsgHandlerArgType(FooDump, P_ARGS) , , ( Debugf("foo: msgDump"); return stsOK; MsgHandlerParametersNoWarning; // FooDump /**************************************************************************** FooNew Respond to msglnit. Create a new object. ****************************************************************************/ MsgHandlerArgType{FooNew, P_FOO_NEWI ( inst; Foo INST memset{&inst, 0, sizeof{FOO_INST»; 1/ Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; //FooNew 1***************************************************** *********************** FooFree Destroy an object. ****************************************************************************/ MsgHandlerArgType(FooFree, P_ARGS) ( return stsOK; MsgHandlerParametersNoWarning; // FooFree 1***************************************************** *********************** FooSave Save self to a file. ****************************************************************************/ MsgHandlerArgType(FooSave, P_OBJ_SAVE) TEMPLATE APPUCAnON I SAMPLE CODE loa ... UI ...III P FOO INST pInst; STREAM READ WRITE fsWrite; FILED DATA filed; STATUS s; pInst = IDataPtr(pData, FOO_INST); memset(&filed, 0, sizeof(FILED_DATA)); filed. style = pInst->metrics.style; II Write filed instance data to the file. fsWrite.numBytes = SizeOf(FILED_DATA); fsWrite.pBuf = &filed; ObjCallRet(msgStreamWrite, pArgs->file, &fsWrite, s); return stsOK; MsgHandlerParametersNoWarning; II FooSave 1***************************************************** *********************** FooRestore Restore self from a file. ****************************************************** **********************1 MsgHandlerArgType(FooRestore, P_OBJ_RESTORE) { STREAM READ WRITE fsRead; FOO INST inst; filed; FILED DATA STATUS s; . memset(&inst, 0, sizeof(FOO_INST)); II Read instance data from the file. fsRead.numBytes = SizeOf(FILED_DATA); fsRead.pBuf = &filed; ObjCallRet(msgStreamRead, pArgs->file, &fsRead, s); inst.metrics.style = filed. style; II Update instance data. ObjectWrite(self, ctx, &inst); return stsOK; MsgHandlerParametersNoWarning; II FooRestore 1***************************************************** *********************** FooGetStyle Get foo style. ****************************************************** **********************1 MsqHandlerArqType(FooGetStyle, P_FOO_STYLE) ( P_FOO_INST pInst = IDataPtr(pData, FOO_INST); MsqHandlerParametersNoWarninq; *pArqs = pInst->metrics.style; return stsOK; II FooGetStyle 1***************************************************** *********************** FooSetStyle Set foo style. ****************************************************** **********************1 MsgHandlerArgType(FooSetStyle, P_FOO_STYLE) { FOO INST selfInst; P_FOO_INST pInst; selfInst = IDataDeref(pData, FOO_INST); pInst = &selfInst; II Update instance data. pInst->metrics.style = *pArqs; ObjectWrite(self, ctx, pInst); return stsOK; MsgHandlerParametersNoWarning; 1/ FooSetStyle 1***************************************************** *********************** FooGetMetrics Get foo metrics. ****************************************************** **********************1 MsqHandlerArgType(FooGetMetrics, PJOO_METRICS) { P FOO INST pInst; pInst = IDataPtr(pData, FOO_INST); *pArgs = pInst->metrics; return stsOK; MsgHandlerParametersNoWarning; II FooGetMetrics 1***************************************************** *********************** CIsFooInit Install the class. ****************************************************** **********************1 STATUS CIsFooInit (void) ( CLASS_NEW new; STATUS s; II Create the class. ObjectCall(msgNewDefaults, clsClass, &new); new.object.uid clsFoo; new.object.key (OBJ_KEY)clsFooTable; new.cls.pMsg clsFooTable; new.cls.ancestor clsObject; new.cls.size = SizeOf(FOO_INST); new.cls.newArqsSize = SizeOf(FOO_NEW); • ObjCallRet(msgNew, clsClass, &new, s); return stsOK; II ClsFooInit II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II II Std Messages - clsFoo II II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II static P_STRING clsFooStrings[] = ( "'I , TEMPLATE.RC 1***************************************************** *********************** templt.rc 'Copyright 1991, 1992 GO Corporation. All Rights Reserved. You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO'THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO YOU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. $Revision: 1.0 $ 13 Nov 1991 18:19:50 $ $Date: This file contains the string table resources used by the standard system message facility, StdMessage. Each resource table represents the strings for the status values for a given class. The administered portion of the status value is used as the resource identifier. The value portion of the status value is used as an index into the string table resource. ****************************************************** **********************1 tinclude linc1ude 'include linclude 'include II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II Std Messages - clsTemplateApp II II II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II static P_STRING clsTemplateAppStrings[] = ( '"', I r None "Error 1", I I stsTemplateAppError1 "Error 2", I I stsTemplateAppError2 pNull I; static RC_INPUT clsTemplateAppStringTable resForStdMsgError(clsTemplateApp), clsTemplateAppStrings, 0, resStringArrayResAgent I; "Error 1", "Error 2", pNull I; static RC_INPUT clsFooStringTable resForStdMsgError(clsFoo), clsFooStrings, 0, resStringArrayResAgent I; II II II None stsFooError1 stsFooError2 II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II II Resource Tables II II * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * II resInput [] P RC INPUT &clsTemplateAppStringTable, &c~sFooStringTable, pNul1 }; MAKEFILE i., •••••• i ••••••••••••• i ••••• i ••••••••••••••••• • • WMake Makefile for the templtap application I • Copyright 1991, 1992 GO Corporation. All Rights Reserved. I • • • • • • • • • You may use this Sample Code any way you please provided you do not resell the code and that this notice (including the above copyright notice) is reproduced on all copies. THIS SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, AND GO CORPORATION EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL GO CORPORATION BE LIABLE TO ~OU FOR ANY CONSEQUENTIAL,INCIDENTAL,OR INDIRECT DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THIS SAMPLE CODE. • • ,$Revision: • $Date: 1.3 $ 07 Jan 1992 16:57:32 $ i i •••••••• ii •••• i ••• ii •••••••• ii •••••••••• i ••••• !ifdef %PENPOINT PATH PENPOINT_PATH = $(%PENPOINT_PATH) TEMPlATE APPUCATION I SAMPLE CODE ... UI 00 loa 0- !else PENPOINT_PATH = \penpoint !endif The DOS name of your project directory. PROJ = templtap Standard defines for sample code (needs the PROJ) definition !INCLUDE $(PENPOINT_PATH)\sdk\sample\sdefines.mif The PenPoint name of your application EXE_NAME = Template Application The linker name for your executable; company-name-V «minor» EXE LNAME = GO-TEMPLATE APP-Vl Object files needed to build your app EXE_OBJS = methods.obj templtap.obj foo.obj Libs needed to build your app EXE_LIBS = penpoint app RES_FILES = template.res Targets all; $ (APP_DIR)\$ (PROJ) .exe $(APP_DIR)\app.res .SYMBOLIC The clean rule must be ;; because it is also defined in srules clean ;; .SYMBOLIC -del methods.h -del methods.tc -del app.res Dependencies templtap.obj; templtap.c templtap.h methods.h foo.obj; foo.c foo.h methods.h methods.obj; methods.tbl templtap.h foo.h Standard rules for sample code !INCLUDE $(PENPOINT_PATH)\sdk\sample\srules.mif o Adder * Adder is a simple pencentric calculator, limited to addition and subtraction. The user can write "4+5" and Adder will print" 4+ 5=9" at the top of its window. In addition, Adder can handle slightly more complicated expressions, such as "42 + 8 + 3 -2.5". * * * * .* * * * I * Obiectives This sample application shows how to: • Create an insertion pad for handwritten input • Create a translator and a custom template for the insertion pad • Translate the insertion pad ink when the user lifts the pen out of proximity Files Used • Disable some of the handwriting engine's assumptions to improve arithmetic recognition • Create a custom layout window ADDERAPP.C • Construct a simple parser. ADDEREvL.C the source code for the adder evaluator engine class Class Overview Adder defines two classes: clsAdderApp and clsAdderEvaluator.1t makes use of the following classes: clsApp clsAppMgr clsClass clsCustomLayout clsIP clsLabel clsObject The code for Adder is in \PENPOINT\SDK\SAMPLE\ADDER. The files are: the source code for the application class ADDEREVL.H the header file for the evaluator class METHODS.TBL the method tables for the adder classes. Calcul(Zllor The calculator application implements a typical pushbutton calculator. This program is split into an application, which handles the user interface, and a calculator engine, which performs the computations. clsXText Compiling To compile Adder, just cdr\penpoint\sdk\sample\adder wmake This compiles the application, and creates ADDER.EXE in· \PENPOINT\APP\ADDER. Running After compiling Adder, you can run it by 1 Adding \\boot\penpoint\app\Adder to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Tapping on the Accessories icon to open it, and then tapping on "Adder." Alternatively, you can boot PenPoint and then install Adder via the Connections Notebook. CALCULATOR I SAMPLE CODE .. l.a 01 .., ..,0Obiectives This sample application shows how to: • Separate out part of an application into a reuseable dynamic link library • Have an application be an accessory • Use ClsSymbolsInitO • Use table layout and custom layout • Use labels • Use TK_TABLE_ENTRY struct to create a collection of buttons in one fell swoop • Handle ButtonNotifystyle messages • Change the default window size • File data. Class Overview Calc defines two classes: clsCalcApp and clsCalcEngine. It makes use of the following classes: Although clsCalcApp does not file any of its views, it does file the string that is displayed in its label. It also files the calculator engine object by sending it msgResPutObject (in response to msgSave) and msgResGetObject (in response to msgRestore). Compiling To compile Calc, just cd \penpoint\sdk\sample\calc wmake Thi~ compiles the dynamic link library and the application, and creates CALCDLL and CALCEXE in \PENPOINT\APP\CALC It also copies CALCDLC to \PENPOINT\APP\CALC Running After compiling Calc, you can run it by 1 Adding \ \boot\penpoint\app\Calculator to \PENPOINT\BOOT\APP .INI 2 Booting PenPoint 3 Tapping on the Accessories icon to open it, and then tapping on "Calculator." clsClass Alternatively, you can boot Pen Point ana then install Calculator via the .Connections Notebook. clsCustomLayout Files Used clsAppMgr clsLabel clsObject clsTkTable The code for Calc is in \PENPOINT\SDK\SAMPLE\CALC The files are: CALC.DLC indicates the dependency of the calculator application upon the calculator engine When clsCalcApp receives msgAppOpen, it creates a set of windows (all of which are standard UI components): CALCAPP.C clsCalcApp's code and initialization • A table of buttons (clsTkTable) for the calculator's push buttons CAtCENG.C clsCalcEng's code and initialization • A label (clsLabel) for the calculator's display CALCENG.H header file for the calculator engine • A window (clsCustomLayout) to hold the label and the button table. CAPPMETH.TBL method table for the application The application destroys these windows when it receives msgAppClose. In its msgAppInit handler, the application creates an instance of clsCalcEngine, the calculator engine. This class performs arithmetic operations. CALCAPP.H header file for the application class CENGMETfi.TBL method table for the engine DLL.LBC list of exported functions for the Watcom linker S_CALC.C symbol name definitions and call to ClsSymbolsInitO. Clock Obiectives This sample application shows how to: Clock is an application that serves two purposes: a digital alarm clock distributed as a part of PenPoint, and a sample application. • Observe the system preferences for changes to dateltime formats • Observe the power switch to refresh on powerup • Provide option cards for an application • Destroy unneeded controls on a default application option card • Disable inappropriate controls on a default application option card • Respond to msgGWinForwardedGesture (including handling, forwarding, and ignoring gestures) • Provide quick help • Make use of clsTimer. Class Overview Clock defines four classes: clsClockLabel, clsClockApp, clsClockWin, and clsNoteIconWin. It makes use of the following classes: clsApp ·clsAppMgr clsAppWin clsClass c1sCommandBar clsDateField clsGotoButton c1sIconWin clslntegerField The end user can configure the clock's display by changing the placement of the time and date and by specifying things like whether the time should include seconds. The enduser can also set up an alarm. Depending on how the user configures an alarm, it might beep at a certain time on a certain day or display a note every day at the same time. clsLabe1 clsNote clsOptionTable clsPopupChoice clsPreferences clsString c1sT ableLayout III .... CLOCK ft • • __ ~_ ~ 0W . III 0- clsT extField Files Used clsTimer The code for Clock is in \PENPOINT\SDK\SAMPLE\CLOCK. The files are: clsTkTable BITMAP.RES clsToggleTable CLABEL.C source code for clsClockLabel resource file (compiled) for the clock accessory icon clsClockApp CLABEL.H header file for dsClockLabel clsNoteIconWin appears as a corkboard on the popup note that Clock displays when an alarm goes off. The note needs to be dismissed when the user opens one of the icons in the window. To provide this functionality, clsNoteIconWin observes objects inserted into its window. CLOCK.RC resource file (uncompiled) for the clock's quick help Clock uses a table layout of several windows. There can be up to four child windows (time digits, am/pm indicator, alarm indicator, and the date). All of these are labels. clsLabel only repaints the rightmost characters that change. So, to minimize flashing as time ticks away, Clock displays the time digits in a separate window from the am/pm indicator. clsClockApp does not file its labels or client window. It does, however, file most of the settings of the controls in its Display and Alarm option cards. It also files its clsNoteIconWin corkboard window. COlllpiling To compile Clock, just cd \penpoint\sdk\sample\clock wmake This compiles the application and creates CLOCK.EXE and APP.RES in \PENPOINT\APP\CLOCK. Running After compiling Clock, you can run it by 1 Adding \\boot\penpoint\app\clock to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Tapping on the Accessories icon to open it, and then tapping on "Clock." Note: Most PenPoint configurations list the Clock application in \PENPOINT\BOOT\SYSAPP.INI. If this is done in your configuration, then you can skip step 1 above. CLOCKAPP.C source for clsClockApp, the application class CLOCKAPP.H header file for the application class CWIN.C source code for dsClockWin CWIN.H header file for clsClockWin METHOD.TBL the method tables for the four classes NOTEIWIN.C source code for clsNoteIconWin NOTEIWIN.H header file for dsNoteIconWin. Notepaper App clsApp clsAppMgr Notepaper App is a simple notetaking application. It relies on the NotePaper DLL for most of its functionality. clsGestureMargin clsNotePaper Compiling To compile Notepaper App, just cd \penpoint\sdk\sample\npapp wmake This compiles the application and creates NPAPP.EXE and APP .RES in \PENPOINT\APP\NPAPP. Running After compiling Notepaper App, you can run it by 1 Adding \\boot\penpoint\app\Notepaper App to \PENPOINT\BOOT\APP .INI 2 Booting PenPoint 3 Creating a new Notepaper App document, and turning to it. Alternatively, you can boot PenPoint and then install Notepaper App via the Connections Notebook. Files Used The code for Notepaper App is in \PENPOINT\SDK\SAMPLE\NPAPP. The files are: BITMAP.RES resource file for the document bitmap METHODS.TBL method table for the notepaper application class MODES.RES resource file for the mode icons Obieclives NPAPP.C source code for the notepaper application This sample application shows how to use the NotePaper DLL. PAPER.RES Class Overview PENS.RES resource file for the different paper icons resource file for the different pen icons. Notepaper App defines one class: clsNotePaperApp. It makes use of the following classes: I NOTEPAPER APP loa o U! SAMPLE CODE Paint . Paint is a simple painting application. The user can choose different nibs (square, circle, or italic) and different paint colors (white, light gray, dark gray, and black) with which to paint. The user can easily clear the window to start painting allover again. • • • 101 01 01 Create a scroll win, and have a gray border displayed around its client window Use pixel maps Handle pen input events. While Paint does demonstrate these topics, it is far from being a perfect sample application, for these reasons: • Pixelmaps are inherently device dependent, so Paint documents are also device dependent • When the user changes the screen orientation, Paint does not flush and rebuild its pixelmaps • Paint's pixelmaps cannot be printed (which is why the Print menu item and the "P" gesture are not supported). Within the field of sampled image programming, there are well understood ways to overcome these problems. However, Paint does not implement them. Class Overview Paint defines three classes: dsPaintApp, dsPaintWin, and dsPixWm. It makes use of the following classes: dsApp dsAppMgr dsChoice dsClass dsFlleHandle .dsImgDev dsMenu dsScrollWin Obiectives This sample application shows how to: • Read and write data and strings to a file • Provide a totally applicationspecific menu bar (no SAMS) • Place a button on a menu line dsSysDrwCtx clsToggleTable dsWm Compiling To compile Paint, just cd \penpoint\sdk\sample\paint wmake Toolkit Demo This compiles the application and creates PAINT .EXE and APP .RES in \PENPOINT\APP\PAINT. Toolkit Demo (also known as tkdemo) shows how to use many of the classes in PenPoint's UI Toolkit. Although it is not exhaustive, it does provide many examples of using the Toolkit's APls and setting fields to get different functionality and visual effects. Running After compiling Paint, you can run it by 1 Adding \ \boot\penpoint\app\Paint Demo App to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Creating a new Paint document, and turning to it. Alternatively, you can boot PenPoint and then install Paint Demo App via the Connections Notebook. Files Used The code for Paint is in \PENPOINT\SDK\SAMPLE\PAINT. The files are: BITMAP.RES CUTIL.C resource file for the document icon utility routines for reading & writing data and strings CUTIL.H header file for reading/writing utility routines METHODS.TBL method table for the paint classes PAPP.C source code for the paint application class PIXELMAP.C utility routines for using pixel maps PIXELMAP.H header file for pixel map utility routines PIXWIN.C source code for the PixWin class PIXWIN.H header file for the PixWin class PWIN.C source code for the PaintWin class PWIN.H header file for the PaintWin class. Tkdemo does not show how to use trackers, grab boxes, or progress bars. Obiectives This sample application shows how to: I • Use most of th¢~lasses in the PenPoint UI Toolkit • Create a table layout TOOLKIT DEMO SAMPLE CODE III ...00 .,o • Create a custom layout clsStringListBox • Provide multiple option cards for an application subclass clsTabBar • Determine if an option card should be applicable, based on the current selection • Provide a bitmap for clslcon • Create a scrollwin as the app's frame client window • Specify an application version number. Class Overview Toolkit Demo defines one class: clsTkDemo. It makes use of the following classes: clsApp clsAppMgr clsBitmap clsBorder clsButton clsChoice clsCustomLayout clsTabButton clsTableLayout clsT extField clsTkTable clsToggleTable clsTkDemo creates a scrollwin as its frame client window. This lets the demonstration's windows be larger than the frame. A scrollwin can have many client windows, but it shows only one at a time. So, tkdemo creates several child windows, one for each of the topics it demonstrates. clsTkDemo also creates a tab that corresponds to each window. The tab buttons are set up so that their instance data includes the UID of the associated window. When the user taps on the tab, the tab sends msgTkDemoShowCard to the application. Tkdemo then switches to that window by sending msgScrollWinShowClintWin to the scrollwin. An interesting point is that, to avoid receiving messages while it is creating windows, tkdemo only sets itself as the client of its tab bar after it has created all the windows. clsField clsTkDemo's instance data is the tag of the current selection and the UIDs of its option sheets. It files all of this in response to msgSave. clsFixedField Co.piling clsFontListBox To compile Toolkit Demo, just clsDateField clslcon clsIntegerField cd \penpoint\sdk\sarnple\tkdemo wmake clsListBox This compiles the application and creates TKDEMO.EXE in \PENFOINT\APP\TKDEMO. clsMenu Running clsLabel clsMenuButton clsNote clsOptionTable clsPopupChoice clsScrollWm After compilingToolkit Demo, you can run it by 1 Adding \\boot\penpoint\app\Toolkit Demo to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Creating a new Toolkit Demo document, and turning to it. .. ----- Alternatively, you can boot PenPoint and then install Toolkit Demo via the Connections Notebook. Files Used The code for Toolkit Demo is in \PENPOINT\SDK\SAMPLE\TKDEMO. The files are: BORDERS.C code for creating different kinds of borders BUTIONS.C code for creating different kinds of buttons CUSTOMS.C code for creating clsCustomLayout instances FIELDS.C code for handwriting fields of clsField and its descendants clsDateField, clsFixedField, clsIntegerField, and clsTextField Inpulapp Inputapp is a simple application that demonstrates penbased input event handling. As the user drags the pen around, inputapp draws' a small square in the window. To provide this functionality, it creates a descendant of clsWm (clslnWin), which looks for pen input events. GOLOGO.lNC include file containing a handcoded GO logo bitmap ICON.RES resource file for a smiley face, created with PenPoint's bitmap editor ICONS.C code for creating different kinds of icons IABELS.C code for creating clsLabel instances with different fonts, rows, columns, etc. LBOXES.C code for making list boxes (clsListBox, clsStringListBox, and clsFontListBox) METHODS.TBL the method table for the tkdemo app NOTES.C creates different kinds of instances of clsNote OPTABLES.C creates a sample option table OPTIONS.C demonstrates option cards and their protocol, and also c~eates an instance of clsPopUpChoice TABLES.C creates various tables (instances of clsTableLayout) TKDEMO.C code for the overall Toolkit Demo application TKDEMO.H header file for the application class TKTABLES.C code for creating several subclasses of clsTkTable, including clsMenu, clsChoice, and clsTabBar. Inputapp's window tracks the pen by drawing a small box at the xy location provided by the event. It also erases the previous box by first setting its DC's raster op to sysDcRopXOR. Then it redraws the box at the previous location, thereby erasing it. Note: If you want your windows to respond to gestures or handwriting, you usually do riot look for input events yourself. Instead, you use specialized window classes such as clsGWin and clsIP, which "hide" lowlevel input event processing I . INPUTAPP SAMPLE CODE ." 010 .., from their descendants. These classes send higherlevel notifications such as msgGWinGesture and msglPDataAvailable. This compiles the application, and creates INPUTAPP.EXE in \PENPOINT\APP\lNPUTAPP. Obiectives Running This sample application shows how to: After compiling inputapp, you can run it by • Create a drawing context in a window 1 Adding \\boot\penpoint\app\inputapp to \PENPOINT\BOOT\APP.INI • Set a window's input flags to get pen tip & move events 2 Booting PenPoint • Handle pen events (PenUp, PenDown, etc.) in a window 3 Creating a new inputapp document, and turning to it. • Use BeginPaint/EndPaint messages • Turn on message tracing for a class. Alternatively, you can boot PenPoint and then install inputapp via the Connections Notebook. Class Overview Files Used Inputapp defines two classes: clslnputApp and clslnWin. It makes use of the following classes: The code for inputapp is in \PENPOINT\SDK\SAMPLE\lNPUTAPP. The files are: clsApp clsAppMgr clsClass clsSysDrwCtx clsWin The only function of clslnputApp is to create clslnWin as its client window. It does this in its msgApplnit handler. clslnWin is a descendant of clsWin. Because clsWin does not turn on any window input flags, clslnWin must set window flags to get certain pen events. Since clslnputApp recreates the input window from scratch and has no other instance data, it does not need to file itself. The input window does not need to save state either. When called upon to restore its state (msgRestore), it simply reini tializes. CompiUng To compile inputapp,just cd \penpoint\sdk\sample\inputapp wmake INPUTAPP.C the source code for the inputapp classes METHODS.TBL the method tables for the classes. .... o Wrilerap Writerap provides a ruled sheet for the user to write on. When the user lifts the pen out of proximity, writerap translates what the user wrote, and places the translated text on the line below the ink. The user can change the translation algorithm from wordbased, to text or numberbased. • Use the xlist returned by the translation objects and how to interpret its data • Put a choice control in a menu. Class Overview Writerap defines two classes: clsWriter and clsWriterApp. It makes use of the following classes: clsApp clsAppMgr clsChoice clsClass clsMenu clsSPaper clsSysDrwCtx clsXText clsXWord. COllipiling To compile writerap, just cd \penpoint\sdk\sample\writerap wmake This compiles the application and creates INPUTAPP.EXE in \PENPOINT\APP\WRITERAP . Running After compiling writerap, you can run it by Oblectives 1 Adding \\boot\penpoint\app\writerap to \PENPOINT\BOOT\APP.INI 2 Booting PenPoint 3 Creating a new writerap document, and turning to it. This sample application shows how to: • Use the SPaper and Translation objects • Make a translator for words, text, or numbers only I Alternatively, you can boot PenPointand then install writerap via the Connections Notebook. WRITERAP SAMPLE CODE ..... ~ ~ .... ~ Files Used 2 The code for writerap is in \PENPOINT\SDK\SAMPLE\WRITERAP. The files are: Tapping on the Connections Notebook to open it, and turning to the Services page 3 Installing Basic Service. METHODS.TBL the method table for the classes WRITERAP.C the source code for the classes WRITERAP.H the header file for the classes. 'esting Basic Service To test Basic Service, check to make sure that you can install and deinstallit without getting any errors in PENPOINT.LOG. Files Used Basic Service is the absolute minimum code required to be a service. This is the Hello World for service writers. It only requires the service writer to implement one message: msgNewDefaults. This approach helps to get someone up and running as a service in the shortest time. The code for Basic Service is in \PENPOINT\SDK\SAMPLE\BASICSVC. The files are: BASICSVC.C clsBasicSvc's code and initialization BASICSVC.H header file for clsBasicSvc For more complex services, see the TESTSVC and MILSVC samples. DLL.LBC list of exported functions for the Watcom linker Obiectives METHOD.TBL method table for clsBasicSvc. Test Service This sample service shows how to make a service (the makefile differs from application makefiles). Class Overview Basic Service defines one class: clsBasicService. It makes use of the following classes: Test Service is a starter kit for most service writers. It has message handler stubs for the most common service messages. For other examples of services, see the BASICSVC and MILSVC samples. clsClass Obiectives clsService This sample service shows how to: COlllpiling To compile Basic Service, just cd \penpoint\sdk\sample\basicsvc wmake This compiles the service, and creates BASICSVC.DLL in \PENPOINT\SERVICE\BASICSVC. • Make a service (the makefilediffers from application makefiles) • Define handlers for messages sent to a class. Class Overview Test Service defines two classes: clsTestService and clsTestOpenObject.1t makes use of the following classes: clsButton Running clsClass After compiling Basic Service, you can run it by clsFileHandle 1 clsOpenServiceObject Booting Pen Point MIL Se[JIVice clsOptionTable clsService Test MIL Service is a starter kit for device driver writers. Compiling For other examples of services, see the BASICSVC and TESTSVC samples. To compile Test Service, just cd \penpoint\sdk\sample\testsvc wmake This compiles the service and creates TESTSVC.DLL in \PENPOINT\SERVlCE\TESTSVC. Obieclives This sample service shows how to make a service (the makefile differs from application makefiles). Class Overview Running After compiling Test Service, you can run it by Test MIL Service defines one class: clsTestMILService. It makes use of the following classes: 1 Booting PenPoint clsClass 2 Tapping on the Connections Notebook to open it, and turning to the Services page clsMILService 3 Installing Test Service Compiling To compile Test MIL Service, just cd \penpoint\sdk\sample\milsvc wmake lesting To test Basic Service, check to make sure that you can install and deinstall it without getting any errors in PENPOINT.LOG. Files Used This compiles the service, and creates MILSVC.DLL in \PENPOINT\SERVICE\MILSVC. Running The code for Test Service is in \PENPOINT\SDK\SAMPLEUESTSVC. The files are: . After compiling Test MIL Service, you can run it by DLL.LBC list of exported functions for the Watcom linker 1 Booting PenPoint METHOD.TBL method table for the classes defined in Test Service 2 Tapping on the Connections Notebook to open it, and turning to the Services page 3 Installing Test MIL Service. OPENOBJ.C clsTestOpenObject's code and initialization OPENOBJ.H header file f9rclsTestOpenObject TESTSVC.C clsTestService's code and initialization TESTSVC.H header file for clsTestService. I Testing To test Test MIL Service, check to make sure that you can install and deinstall it without getting any errors in PENPOINT.LOG. MIL SERVICE SAMPLE CODE ...It.'lCol ~ ~ Files Use" The code for Test MIL Service is in \PENPOINT\SDK\SAMPLE\MILSVC. The files are: DLL.LBC list of exported functions for the Watcom linker METHOD.TBL method table for clsTestMILService MILSVC.C clsTestMILService's code and initialization MILSVC.H header file for clsTestMILService. Here are some of the terms used throughout the PenPoint Software Developer's Kit. Glossary terms appear in bold for emphasis. Terms that are only used within one subsystem may not appear in this Glossary. The glossary entry is followed by the name of the manual part that discusses the topic in more detail. Use the Master Index found at the end of the PenPoint Development Tools volume to locate more information. abstract class A class that defines messages or provides useful functionality, however, clients do not create instances of an abstract class. Class Manager accessory An accessory document "floats" on the Desktop when active, appearing over pages in the Notebook. Most accessories appear in the Accessories auxiliary notebook. Application Framework acetate plane, acetate layer The window system maintains a global, screen-wide display plane called the acetate plane, which is where ink from the pen is normally "dribbled" by the pen-tracking software as the user writes on the screen. Windows, Input activation The transition of a document to an active state, with a running process, an application instance, etc. Application Framework activation What happens when the user actually operates a control, often by lifting the pen up. The user can preview a control without activating it. UIToolkit advisory message Messages that an object's class sends to other objects (possibly including selJf informing them of changes. Often the class sending the message does not itself do anything in response. Class Manager aliasing Multiple tasks can access the same piece of physical memory by aliasing selectors. System Services ancestor Every class has one immediate ancestor. When a class receives a message, the class can elect to pass the message on to its immediate ancestor, and in turn the ancestor may passon the message to its own ancestor. Hence a class can "pick up," or inherit the behavior of its ancestors. Class Manager API (Application Programmer's Interface) The programmatic interface to a software system. The PenPoint API is covered in depth in the Architecture Reference; the functions and messages comprising the PenPoint API are listed in the API Reference and in the header Hles in \PENPOINT\SDK\INC from which the API Reference was derived. all application Usually, the program code and support files of an end-user program-the software that you create to run under PenPoint. all application class A PenPoint class that contains the code and initialization data used to create running applications. Application Framework application framework See "PenPoint Application Framework." (Application Framework) application hierarchy Whenever the user creates, moves, or copies a document, PenPoint creates a directory for the document in memory (usually on the RAM volume, under \PENPOINT\SYS\DT). These directories make up the application hierarchy. Every document has a place in the application hierarchy even if it isn't running, so that the state of the system can survive application shutdown and reboots. The Notebook and Section Table of Contents display a view of part of the application hierarchy. Application Framework application home An external volume on which an application is permanently stored. Installation application instance Loosely, a single, activated document, in a running process, complete with its objects and application data. Strictly speaking, the application instance is only the instance of the application's class, also known as the application object. Application Framework application object The instance of an application that receives PenPoint Application Framework messages when a document of that application is activated. Application Framework at-least-once delivery Guarantees that datagrams are delivered to their destination at least once. Remote Interfaces atom The Text subsystem provides a mechanism for creating a database of unique strings and accessing those strings through a globally-valid, compact, unique identifier, called an atom. Text attribute Various typographical properties affecting the appearance of glyphs in a font, such as bold, italic, condensed, encoding, and so on. Windows and Graphics attributes Data associated with a node in the File System. There is a fIXed set of Hle-system attributes, and clients can define and set additional arbitrary client-defined attributes. File System attributes Text attributes are either default attributes, which apply to an entire object, or local attributes, which apply to contiguous ranges of characters or paragraphs. Text .baseline Windows have a baseline in both the x and y dimensions which you can use to achieve more pleasing alingment. Windows and Graphics 276 GLOSSARY Bezier curve A curved line formed from two end points and two control points, supported by SysDC. Graphics bind You must bind a DC to a window (and hence a pixel device) before you can draw using it. Windows and Graphics binding The process that joins a device driver to a port or another device driver; for example, a client binding to a service makes the client known to that service. Remote Interfoces bitmap An array of pixels, with an optional mask and hot spot. Graphics blocking protocol A transfer protocol used to tranfer large amounts of data, in which clsXfer may block the sender until the receiver empties the buffer. Utility Classes bounding box The smallest rectangle that fully encloses a region. UI Toolkit bridge A computer or other smart device that can link two networks and route traffic from one zone to another. Remote Interfoces browser A component that displays file system contents to the user; used in disk viewers, installers, and TOCs. Utility Classes button Buttons are labels that the user can activate. UI Toolkit cached image A pixelmap in memory which is in the optimum form for quickly displaying in a window. Windows and Graphics capability A flag controlling an action on an object (such as sending it msgNew, creating a descendant class from it, etc.). If it is not set, the action may require that the sender know the object's key (classes) child window A window that is a child, or grandchild, of another in the window tree. Windows choice A table that displays several alternatives and allows the user to pick only one. UI Toolkit class A special kind of object that implements a particular style of behavior in response to messages. Most classes act as factories for objects: you can create instances of a class by sending that class the message msgNew. In the PenPoint Class Manager, a class has a method table, which determines which messages sent to objects of that class that the class responds to. A class's processing of a message often involves passing the message to the class's ancestor in order to inherit appropriate behavior. Class Manager Class Manager The code that supports the object-oriented, message-passing, class~based programming style used throughout the PenPoint operating system and in all PenPoint applications. The Class Manager itself implements two classes, clsObject and clsClass. Class Manager client The general term for any software using some feature of PenPoint. all client The object that receives messages from toolkit components notifYing it of important user actions and events. UlToolkit client window Frames and scrollwins manage a window that is supplied by, or of interest to, the client of the frame or scrollwin. UI Toolkit" clipping The process by which drawing operations in a window are prevented from affecting certain pixels on the windowing device, for example because those pixels are part of another window. Windows and Graphics clsApp Class for all applications; provides a "head start" framework by responding to the PenPoint Application Frameworlls protocol of messages. Application Framework column A Table Server table has a fixed number of columns and a variable number of rows. Components compo~ent A piece of system or application software functionality with a well-defined external interface, packaged as a OLL, which can be used or dynamically replaced by a third-party developer. Some components are unbundled and must be licensed separately from PenPoint. all component layer The component layer of PenPoint consists of general-purpose subsystems offering significant functionality that can be shared among applications. Overview connected A file system volume that is accessible from a Pen Point computer is connected: a volume may be known yet disconnected. File System connection An association between two sockets. Remote Interfoces connection-oriented communication In this style of interface, you establish a connection with another socket, exchange data, and then break the connection. Remote Interfoces connectionless communication In this style of interface, data is transferred from one socket to another without explicitly establishing a connection. Remote Interfoces constraints Specifications for the sizing and positioning of child windows during layout. The UI Toolkit includes clsTableLayout and clsCustomLayout, which implement tabular layout and relative positioning respectivdy. Windows and Graphics container application An application whose primary purpose is to contain (embed) other documents. Application Framework context Information maintained by the driver and slave in a traversal engine episode. Application Framework context Information maintained by the Class Manager to keep track of messages passed up and down the class hierarchy during message processing. Class Manager GLOSSARY 277 context The PenPoint Source Debugger maintains a context indicating the current procedure body, which controls the lexical scope of variables. Development Tools dimensions A custom layout specification has four "dimensions": horizontal location, vertical location, width, and height. UI Toolkit control dirty Indicates whether or not the control has been "touched." ( U1 Toolkit) directory A directory is like a catalog; it contains references to zero or more nodes, called directory entries. File System copy A mode ofITC that copies the entire message; see also transfer. System Services directory handle An object that references either a new or existing directory node in the File System. File System cork margin An optional thin strip in the default application frame that knows how to embed applications. dirty control See "control dirty." (UI Toolkit) Application Framework corrupt When a handle's target node is deleted or destroyed, the File System marks the handle corrupt. File System current byte position Each file handle maintains a position in the file it refers to, the position at which the next byte will be read or written. File System current directory entry Each directory handle maintains a reference to the next directory entry it will use when the directory is read one entry at a time. File System current grafic A picture segment maintains an index in its set of grafics which is the grafic relative to which the next operation will take place. Windows and Graphics data object An object that maintains, manipulates and can recursively file data. Any descendant of clsObject can do this. Often used in Applications together with a view that observes the data object. Application Framework data resource Contains information saved as a stream of bytes; see also object resource. Resources DB The PenPoint Source-level Debugger. DB DC (Drawing Context) An object that implements an imaging model; it draws on the device of the window to which it is bound. GO's SysDC is the imaging model used by all PenPoint's visual components. Graphics deactivate Deactivating an application removes its code from the user's PenPoint computer, but the Installer still keeps track of the application's UID and where its home is. PenPoint Application Framework, Installation debug flags A set of flags in PenPoint used to control the behavior of code. You can set each flag to different bit values before and while PenPoint is running, and your code can examine flags programmatically. The lowercase flags are available for outside developers. Application Writing Guide default resource agent A resource agent that treats data resources as a stream of bytes and which treats object resources as a simple object. Resources descendant class A class that inherits from another, either directly, or through a chain of subclasses. Class Manager device driver Provides an interface between programs and devices or other device drivers. Remote Interfaces dirty layout A client can mark a window's layout dirty to indicate that it needs to be laid out. Windows dirty window The window system marks regions of a window "dirty" when they need to be repainted. Dirty windows later receive msgWinRepaint telling them to repaint their contents. You can mark windows as dirty yourself to make them repaint. Windows document A filed instance of some application. A document has a directory in the application hierarchy, but at any given time it may not actually have a running process and a live application instanct'-these usually are destroyed when the user .turns the page. Most documents live in the Notebook, but running copies of floating "applications" such as the Calculator and Installer are also documents. Application Framework dribble The "ink" from the pen when the user writes over windows which support gestures andlor handwriting. Input driver The object requesting the traversal (such as the traversal engine or the search and replace application); see also slave. Application Framework DU4 (Device Units 4th quadrant) The coordinate system of pixels on the screen. Usually you perform window operations in LWC and specifY drawing coordinates in LUe. Windows em A typographical term for the height of a square roughly the size of characters in a font; also known as the point size of the font. Windows and Graphics embed The PenPoint Application Framework provides facilities for applications and components to display and operate inside of other applications and components without detailed knowledge about each other. For example, every page in the Notebook is actually a document embedded in the Notebook's window. As another example, a business graphic document or component could be embedded within a text document. Application Framework embedded document An embedded document is a document that is contained within another document. Application Framework embedded window mark clsEmbeddedWin provides an embedded window mark that indicates the location of an embedded window. Application Framework 278 GLOSSARY entries The items in a list box. List boxes are scrolling windows that support very large numbers of items, not all of which need to exist as windows at all times. UI Toolkit event The occurrence of some an activity, such as the user moving the pen or pressing a key. input exactly-once delivery A style of datagram communication that guarantees that the datagrams are delivered to their destination exactly once. Remote Interfaces explicit locator A locator that includes both a starting point and a path. File System extract The removal of a window and its children from the tree of windows on some device. It makes the window invisible but does not destroy it. Windows and Graphics fields Labels that you can handwrite in. UI Toolkit file A file is a repository for data. File System file export A mechanism of the Browser that presents the user with a choice of file format translators to export the selection. Utility Classes file handle The object with which you access a file node and its data (the handle is not a file itself). File System file import A mechanism of the browser that presents the user with a list of available applications that can accept the imported file. Utility Classes filing Objects must ordinarily file their state in the File System so that the user is not aware of documents activating and terminating on page turns. Application Framework filter A means of restricting the kinds of messages an object or process receives. Input fixed-point number A 32-bit number composed of an integer and fractional component. Windows and Graphics floating A floating window appears above the main Notebook; unlike documents on pages in the Notebook, the user can move and resize a floating window. all flow control Some protocol is necessary to avoid buffer overflow in communication. The PenPoint serial driver utilizes two flow control protocols: XONIXOFF flow control and hardware RTS/CTS flow control. Remote Interfaces font cache After ImagePoint renders a font glyph into a bitmap, it keeps the bitmap in a font cache to speed future drawing of the character at that size. Windows and Graphics frame. The border surrounding documents and option sheets, which often includes a title bar, resize corner, move box, etc. UI Toolkit function prototype The declaration of a function and its parameters in a PenPoint header file. all gateway A computer that translates network requests from one protocol to another. Remote Interfaces gesture A simple shape or figure that the user draws on the screen with the pen to invoke an action or command. Also see scribble. Handwriting global memory Memory accessible from all tasks -- you can pass pointers to objects in global memory between tasks. all glyph A symbol or character in a font. Windows and Graphics grab Getting exclusive notification of events in the system, for example when tracking the pen. Input grafic The individual figure drawing operations stored in a picture segment. Windows and Graphics graphics state The current scale, rotation, units, foreground and background colors, line thickness, and so on, maintained· by a SysDC object. Graphics hardware task A task that is not scheduled by the operating system but by hardware events. This category includes interrupt tasks. System Services heap A pool of memory; individual chunks of memory in it aren't protected. System Services hot mode A state in which the PenPoint Application Framework will not terminate (shut down) an application. Application Framework hot spot The pixel at the "origin" of a bitmap that is drawn at the location of the bitmap. Windows and Graphics image device A windowing device whose image memory is under the control of the client (instead of on a screen or printer). Windows and Graphics in-line In-line fields provide full handwriting and gesture recognition, allowing the user to write with the pen directly into the field itself. UI Toolkit Internationalization The process of generalizing a program so that it is suitable for use in more than one country. Application Writing Guide InBox PenPoint's InBox and OutBox services allow the user to defer and batch data transfer operations for later execution; they appear as iconic notebooks. Remote Interfaces inheritance A class inherits the behavior of its immediate ancestor class. Through inheritance, all classes forms a tree with clsObjectat the top. Class Manager input registry The set of objects that have expressed interest in receiving input events. Input and Handwriting Translation insertion pad A window that supports character entry. It may contain windows supporting different kinds of character entry such as character boxes, ruled paper, and a virtual keyboard. Handwriting GLOSSARY 279 installation Usually refers to the process of installing some item onto a PenPoint compilter, especially an application, but also fonts, handwriting prototypes, and services. Installation localization The process of modifying an application so that it is usable in a specific language or region. Application instance Every object is an immediate instance of the class that created it. It is also an instance of that class's ancestors. For example, a button is an instance of clsButton, but it is also an instance of clsLabe~ of cis Win, and of clsObject. Class locator Specifies a node in the File System; it is a directory handle: path pair, in which the path is the path from that directory handle to the node. File System Manager instance data Data stored in an object; it is normally only accessable by the object's class, which uses instance data in responding to messages sent to that object. The class defines the format of the instance data. Classes often choose to have instance data include pointers to instance "information" stored outside the object. Class Manager inter-task message An ITC message that contains miscellaneous information for a task along with an optional, variable-length message buffer. System Services interrupt task A hardware task that executes in response to some hardware interrupt, such as the interval timer. System Services ITC (Inter-Task Communications Manager) Handles sending data and semaphores between tasks. Note that ITC messages are different from Class Manager messages. System Services kernel The portion of the PenPoiiu operating system that interacts directly with the hardware; the core memory and task management code that is the first code loaded when PenPoint boots. Most System Services are implemented in the kernel. System Services Writing Guide logical device driver A device driver that controls another device driver, rather than an actual device. Remote Interfaces LUC (Local Unit Coordinates) Arbitrary coordinates associated with a DC. You can specify different units, scaling, rotation, and transformation for LUe. Windows & Graphics LWC (Local Window Coordinates) The coordinates of a window in pixels, with the origin at the lower-left corner of the window. Windows & Graphics main window The window of an application which the PenPoint Application Framework inserts on-screen in the page location or as a floating window. An application's main window is usually a frame. PenPoint Application Framework, UIToolkit manager An installation manager is an instance of clsInstallMgr that manages the installation, activation, deactivation, and "Update from Home" of a set of similar items. Installation manager An object that coordinates the previewing and activation of one or more UI components in order to create, for example, an exclusive choice or pop-up menu. UI Toolkit mask Bitmaps have a I-bit deep mask that controls which pixels in the bitmap are drawn. Windows and Graphics key If an object's class doesn't have the capability set for an operation, the sender may still be able to perform it by supplying the correct key. Class Manager memory-mapped You can map a file into memory so that you read and write to it simply by accessing memory. File label A window that displays a string or another window. UI· memory-resident Volume residing in RAM. File System Toolkit menu bar A frame has an optional menu bar below its title bar. The PenPoint Application Framework defines standard application menu items (SAMS) for an application's main window frame. UI Toolkit layout The process of sizing and positioning a tree of windows. Windows and Graphics implements a protocol through which a client can tell windows to layout and windows can ask each other for their desired sizes. Instead of specifying the exact position and size of all its windows, you need only supply a set of constraints on their relative positions. UI Toolkit list An object that holds an ordered collection of items. Utility Classes list box A scrolling window that displays a subset of entries from a potentially very large set. UI Toolkit local memory Per-process memory; pointers to objects in local memory can only be passed between tasks in the same task family. all local volume Volumes on hard or floppy disk drives attached to the PenPoint Computer through its built-in SCSI port. File System . System menu button A button that displays a pop-up menu when the user taps on it. UI Toolkit message A 32-bit value you send to an object requesting it to perform some action. Messages are constants representing some action that, an object can perform. The type MESSAGE is a tag which identifies the class defining the message and guarantees uniqueness. When you send a message to an object, if that message is mentioned in the class's method table, then the Class Manager calls a message handler routine in the class's code which responds to the message. Class Manager message argument A U32 parameter to sending a message which lets the sender specify additional information, Class Manager 280 GLOSSARY message argument(s) The information needed by a class to respond to a message. Often the message argument parameter is a pointer to a separate message arguments structure: this is the only way a class can pass back information to the sender. Class Manager message handler A function in a class's code that implements appropriate behavior for some message' or messages; called by the Class Manager in response to message associated with it in the class's method table. Class Manager message tracing Facility that prints out all messages received by a particular instance or class. Class Manager method Synonym for message handler. Class Manager method table An array of message-function name pairs (plus some flags) that determines which message handler function (if any) will handle messages sent to objects of that class. Class Manager Method Table Compiler DOS program that compiles a file of method tables into an object file that you link with your classes' code. Class Manager metrics Information made public about instances of a class is often called metrics, and many classes provide a pair of messages to set and get metrics. all name Most names in PenPoint, such as the names of nodes in the File System, are strings containing 1 to 31 characters. all node A location in the File System; can be a directory or file. The PenPoint file system is organized as a tree of nodes. File System note A window that presents transient information to the user. UJ Toolkit Notebook The main notebook on-screen, usually the user's personal notebook. all notebook metaphor The visual paradigm in PenPoint of a physical notebook containing pages documents and sections, with tabs, a page turn effect, and so on. PenPointApplication Framework, UJ Toolkit notification The act of sending a message to some client telling it about a change, such as a tap on a button, or a change to an observed object. Class Manager object An entity that maintains private data and can receive messages. Each object is an instance of some class, created by . sending msgNew to the class. Class Manager object resource Contains information required for creating or restoring a PenPoint object; see also data resource. option sheet A floating frame that displays attributes of the selection in one or more "card" windows. UJ Toolkit oudine ImagePoint stores fonts as size and resolution-independent oudines. It must convert the outline of a glyph into a bitmap before drawing the glyph. Windows and Graphics owner An object is owned by the task that creates it. Class Manager owner The process that creates a sub task owns that sub task and any sibling subtasks created by it. System Services parent window Every window in the window tree but the root window has a parent window. Conversely, when you extract a window from the window tree, it no longer has a parent and so it and all its child windows are no longer visible on-screen. Windows pass back Nearly all classes return a STATUS value in response to a messages. In addition, a class may pass back information to the sender in the message's message arguments. Class Manager path The sequence of node names between a directory handle and a particular node. The path to a node in the File System is the sequence of directory names and the node's own name that the File System must traverse to get to that node from a particular directory handle; the directory handle:path pair is called a locator. File System PenPoint GO's operating system that supports pen-based computing in a document-oriented notebook-like interface. all PenPoint Application Framework Both the protocol supporting multiple, embeddable, concurrent applications in the Notebook, and the support code that implements most of your application's default response to the protocol for you. The protocol and code provide a "head start" for building applications in the pen-based, document-oriented Notebook environment. Application Framework picture segment An object in which you can store and replay sequences of drawing operations. Windows and Graphics pixel A picture element with a value. Windows and Graphics pixel device An abstract class that defines the minimal behavior for a pixelmap graphics device. A pixel device corresponds either to actual hardware capable of displaying graphics, or memory that simply stores an image (an image device). Windows and Graphics pixelmap A rectangular array of pixels. Windows and Resources Graphics observer An object that has asked the Class Manager to notifY it when changes occur to another object. Objects maintain a list of their observers. Class Manager point A unit of measure, approximately equal to 1172 of an inch. Windows and Graphics . open A document currently displayed on-screen. Application Framework pop-up. A window (usually a menu or field) that temporarily appears on top of all other windows. UJ Toolkit GLOSSARY 281 previewing The feedback provided by a control while the user is "fiddling" with the control, before the user activates (if at all) the control. UI Toolkit resource definition file A C-like file which when run through the PenPoint Resource Compiler creates or appends to a resource file. Resources ,privilege A level of trust that PenPoint has in code. Some data and functions are protected in that they can only be accessed by code at a high privilege level. A task always executes at some privilege level. System Services resource file A file containing typed resources, each identified by UUID. Every application and document has a well-known resource file. Clients can ask a resource' file to read and write resources, and to search for a particular resource. Resources process An operating system context with its own local memory. System Services process ID String that identifies a process in DB. DB protected Usually refers to memory locations which can't be accessed, either because they require system privilege or are "hidden" in another process's local memory. 286 prototype A shape template with which sets of strokes are compared in handwriting recognition. handwriting proximity A state reported by the pen hardware on some PenPoint computers when the user has the pen near the screen. It's independent of the pen tip being "down." Using a mouse, you simulate this by pressing the MIDDLE mouse button to go out of proximity. input PWC (parent Window Coordinates) The LWC (Local Window Coordinates of a window's parent). Windows and Graphics raster op Determines how the values of a source and destination pixel combine to affect the value of the destination pixel in graphic operations. Graphics resource list A list of resource files. Each document has a resource list composed of its own resource file, its application's resource file, and the system resource file. Clients can ask a resource list to search for a particular resource. ~ Resources RGB (Red, Green, Blue) A means of specifying colors by the amount of these primary colors. Windows and Graphics Ring An area of memory protection on the Intel 80386. Most ap'plication code runs in Ring 3 memory; crucial parts of the PenPoint kernel and device drivers run in Ring 0 memory. root directory Top-most node of the file system hierarchy on a volume. File System root window Top of the window tree on a windowing device. Windows and Graphics router A task that decides which window object to send input events to. input row A Table Server table has a fixed number of columns and a variable number of rows. Components recognition Matching a set of user strokes with the most likely prototype(s) during handwriting translation. sampled image An image made up of pixels. Windows and handwriting Graphics region Area of a window that is visible or requires repainting (dirty region). Windows 6- Graphics SAMs (Standard Application Menus) The PenPoint Application Framework supplies a set of SAMS (the Document and Edit menus), to which applications can add their own menu items. PenPoint Application Framework, UI remote server A program or device on another computer that can communicate 'over the network. Remote Interfaces remote volume Volumes available over a network or other communication channel. File System render ,The generation of a bitmap of a font glyph from an oudine. ImagePoint can synthesize certain attributes of the font, for example rendering bold characters from a regular outline. Windows and Graphics repaint The pixels of a window need to be repainted in various circumstances: when the window first appears on screen, when the window is covered by another window and then exposed, when the window changes size, and so on. When a window needs repainting, the window system marks it dirty. When you repaint a window, the pixels affected·are the visible portions of the dirty region. Windows and Graphics resource A uniquely identified collection of data. Resources allow applications to separate data from code in a dean, structured way. Resources Toolkit scope The component, documents, set of documents, or partial area of the same upon which a traversal acts. PenPoint Application Framework, Utility Classes scribble A collection of strokes which translators can translate into either text characters or command gestures. input SDK (Software Developerment Kit) A development package to assist developers in writing applications for a system. The PenPoint SDK provides the code required to develop applications, and documentation and tools to assist development. all selection PenPoint maintains a system-wide selection, which is the target for all editing operations. The Notebook UI lets the user select applications and icons; applications and components may allow the user to select words, shapes, and other entities within their windows. Utility Classes 282 GLOSSARY self The object that originally receives a message. Code that processes a message is passed the UID of self. Class Manager much higher resolution than that of the "ink" dribbled by the pen on-screen. input semaphore A lock that lets cooperating tasks synchronize access to resources, or guarantee exclusive access to one process or subtask at a time. System Services style fields You can control the appearance and behavior of many UI Toolkit components by specifYing different parameters in the style fields of its class and ancestor classes. service A general, non-application DLL that enables PenPoint clients to communicate with a device or to access a function, such as a database engine. Remote Interfaces service section A section in the Inbox or Outbox; each is associated with a specific service and represents a queue to or from that service. Remote Interfaces shut down The PenPoint Application Framework shuts down a document to conserve memory by destroying its application object and terminating its process. Thereafter the document only exists as a directory and files in the application hierarchy. Application Framework socket To access the network, you must create a socket, through which you send and receive transport messages. Remote Interfaces ' software task A task that is scheduled for execution by software. This category includes processes and subtasks. System Services source-level debugger A debugger (such as PenPoint's Source Debugger) that lets you use line numbers, variables, and structures from the original source code instead of from the assembly language in executable code. DB SPaper (sketchpad) A view window that stores the user's handwriting in a scribble. Handwriting standard message A procedural interface to put up standardized notifications, warnings, and requests to the user in the form of notes. The text of standard messages is stored in resourCes. UI Toolkit stateful Most objects are stateful, that is they change behavior over time. This means that they have state which they need to maintain, and often file as well. all stationery Application-specific template documents. Application Framework UIToolkit subclass To create a new class that inherits from an existing class. You subclass a class in order to take pick up its behavior, while modifYing or extending its behavior to do what you want. Class Manager subtask A task that shares the address space (local memory as well as global memory) of its parent process. System Services SysDC (System Drawing Context) PenPoint's standard DC which implements the imaging model used by all of PenPolnt's visual components. It supports polylines,splines, arcs, outline fonts, arbitrary units, scaling, transformation, and many other features. It unifies text with other graphics primitives in a single, PostScript-like imaging model. Graphics system layer The system layer of PenPoint provides windowing, graphics, and user interface support in addition to common operating system services such as filing and networking. Overview system privilege A high privilege level associated with executing code; particular data may only be accessible by tasks running at this level. Only PenPoint code executes at this level. System Services tag A unique 32-bit number that uses the administrated value of a well-known UID to ensure uniqueness. Class Manager tag An arbitrary 32-bit number that you can associate with any window. You can check a window's tag and search for a particular tag in the window tree, which makes tags useful for identifYing components in shared option sheets and menus. Windows and Graphics tags The PenPoint SDK includes the PenPoint Online Reference Tags facility, which lets you quickly jump to message and structure definitions in the PenPoint header files. Developer Tools status value Most functions and messages in PenPoint return a value of type STATUS, indicating success, an error of some sort, or some other status. Status values are constant tags in order to indicate the class (or pseudo-class) returning the status, and to guarantee uniqueness. all tap A pen down event followed by a pen up, with no significant motion in between. user interface stream A stream operation is one in which files or data items are treated as a series of individual bytes. Utility Classes System Services stroke SysDC strokes a line when drawing lines and the borders of figures using the current line pattern, width, end style, corner style, etc. of the graphics state. Graphics stroke Data structure that stores the path traced by the pen when the user holds it against the screen and "writes" with it. Note that the pen hardware supplies stroke coordinates at task Generic term for a thread of control executing code in PenPoint; includes software tasks and hardware tasks. task family A process and all its subtasks. System Services task ID Hexadecimal identifier of a task in DB. DB testing UID A well-known UID, predefined by GO, that you can use while testing a prototype application. These UIDs have the names wknGDTa through wknGDTg. GLOSSARY TOC (Table of Contents) The browser page at the beginning of a notebook or section that shows its contents. Application Framework toolkit table Workhorse class in the UI Toolkit for a tabular collection of other components; its descendants include choices, option tables, menus, tab bars, and command bars. You can define toolkit tables statically, so they form a simple user interface specification language. UI Toolkit transfer A mode ofITC which queues an alias for the message; see also copy. System Services transfer type A data transfer type identifies a specific data format (such as a string or a structure) and transfer protocol. Utility Classes translator An object that when hooked up to a handwriting window receives captured scribbles and translates them into ASCII characters or gestures. Input and Handwriting Translation UI (User Interface) all UI component Any window implemented by one of the UI Toolkit's many classes. UI Toolkit UI Toolkit PenPoint's User Interface Toolkit provides many different kinds of window subclasses to support a wide variety of on-screen controls, such as labels, buttons, menus, frames, option sheets, etc. UI Toolkit UlD Status values, messages, tags, and input device codes borrow the format of well-known Class Manager UID's to associate themselves with the class that defines them, and to guarantee uniqueness. all UID (Unique Identifier) A 32-bit number that is t71e handle on an object. When you send a message to an object, you send it to the object's UID. Class Manager update region The portion of a window whose pixels are affected by drawing operations. Usually it's either the visible pixels in the window, or the visible pixels in the window that are dirty. Windows user The human being interacting with the software. all UU1D Universal Unique Identifier. A 64-bit number that is guaranteed to be unique across all PenPoint computers, usually used to identify resources in resource files. Resources view A window that presents a user interface and observes a data object; when the data change, the data object notifies its observers and the view updates its display of the object. Application Framework view-data model An approach to designing applications and components that divides the presentation and storage of state into separate view and data objects. Application Framework volume A physical medium or a network entity that supports a file system. File System 283 well-known An object whose UlD is statically defined for all PenPoint computers. Access may still not be possible if the object is not correctly installed on a particular PenPoint Computer. Most PenPoint classes and globally-accessible objects (such as theScreen, theWorkingDir, etc.) have well-known UID's. Class Manager well-known Well-known resource IDs identify data and· object resources that can be used by any client. Resources window tree The hierarchy of windows formed by a window, its child windows, their child windows, and so on. The on-screen window tree starts with a root window on a windowing device. Windows windowing device A pixel device that supports multiple overlapping windows. All windows are associated with some windowing device, even if the window is not currently inserted in the window tree on that device. Windows and Graphics working directory A directory handle pointing to the directory in the application hierarchy for a document. This is where the application should file itself. Application Framework wrapper application A special type of application that handles output for a specific service. Remote Interfaces you In the SDK "you" generally refers to the reader, who we assume to be a programmer who wishes to develop software for PenPoint. Hello there! all zones A network is divided into two or more wnes when it is joined by a bridge. Remote Interfaces Todd Agulnick Bonnie Albin Roland Alden Arvind Arakeri Kenji Armstrong Josh Axelrod AyseAysoy Jennifer Bailey GaryBarg Steve Bartlett Trudy Bartlett Simon Bate Sandy Benett John Bennett Keith Bentley Debbie Biondolillo Sharin Blair Joe Bosurgi Murray Bowles Bill Bradley Alex Brown Howard Brunnings Kurt Buecheler Bill Campbell Lynn Carpenter Robert Carr Karen Catlin Chia-Lun Chang Atri Chatterjee Shirley Chu Mark Cogdill Kevin Doren Gary Downing Andy Felong Jim Floyd Robb Foster Bob Fraik Dan Fraisl John Garrett Alex Gerson Regis Gratacap Steve Grey Ken Guzik Philip Haine Julie Hawthorne Rob Hayes June Hirai Deborah Hodge Tony Hoeber Thorn Hogan Mike Homer Greg Hullender Steve Isaac Jerry Kaplan Ram Kedlaya Susan Keohan Nora Kim Kevin Kitagawa Sheridan Klenk Randy Komisar Omid Kordestani Merri-Lynn Kubota Roy Kumar Ilona Leale James Lee Mark Lentczner Charlie Leu Debra Levesque Dan'l Lewin Debbie Lovell David Low Pete Ma Arjen Maarleveld Paul Marcos Cary Masatsugu Holli Maxwell Janet McCandless Devin McKinney Carl Meyer Norm Meyrowitz George Mills Miki Murdoch Ravi Narayanan Mark Nudelman Lisa Oatman S Page Grace Pariante Elizabeth Parker Craig Payne David Polnaszek James Roseborough Mark Sapsford Michael Sattler Steve Schoettler Don Schuerholi Bruce Schwartz Mike Schwartz David Serna Tetsuo Seto Danny Shader Pam Shear Sandra Shmunis SiewSim Eddie Soloko Peter Stahl PeggyStok Craig Taylor Sue Toeniskoetter Mike Tyler Catherine Valentine Bob Vallone Doug Van Duyne Joe Vierra Scott Walker Tony Wang Darrah Westrup Tim Wiegman Rick Wolfe Phil Ydens Darren Yee Eddie Yee Satya Yenigalla Peter Yorgin Lang Zerner John Zussman And Thanks To Henry Allen Rolando America David Baird Celeste Baranski Fred Benz Timothy W. Bishop Judy Bligh Haydon Boone Adrian Britt Harrilyn CalIon David Chavez Ratha Chea John Croll Art Crumpler Richard Dickson Lisa Forman Harmon Gee Christopher Glazek Michael Gonzales Joanne Gozawa Clifford Gross Paul Hammel Brian Hurley Josh Jacobs Madeleine Kahn Lili Kanda Donna Kelley Henry Korman Liz Landreth Kristina Lincicome Mike McGeoy Henry Madden Marcia Mason Scott Merkle Robert Moncrieff Jerilee Morse Linda Norris Alex Osborne Michael Ouye DougPasos Jonathan Price Paul Quin Steve Rath Pat Rogondino John Russell Alan Saichek Pam Shandrick Robbie Shepard Greg Stikeleather Andres Tong Ben Wiseman Abs macro, 78 Abstract messages, 57 Accessories palette, 104, 134 window, 23 see also Floating, accessories Access Speed, 39 Acetate plane, 9 Activating, documents, 23, 36-37 ADC labs, 195 Adder, 260-261 Address Book, 40 Address Lis£; 13 Add structure, 46-47 Advisory messages, 57 Ancestor class, 43-44 CLASS_NEW message argument and,54 initializations and, 118-119 message handling and, 44 messages, 60 _NEW_ONLY structure and, 50-51 selfUIDs and, 56 see also Classes AppleTalk protocols, 8 Application activating, 107 classes, 30 compiling and linking, 66-67 Empty Application, 92-94 components, 23, 40, 149~150 data, 35 debugging, 68 designing, 59-61 developing, 59 strategy, 64-66 directories, 28, 29 embedded applications and, 35 document, 37 documenting, 175 embedding, 34-35 " enhancements, 163 ' environment, 20-21 hierarchy, 28-35 application data, 35 defined,28 Desktop, 33 'embedded applications, 34-35 floating accessories, 34 Notebook, 33 page-level applications, 33-34 sections, 34 initializing, 22-23 installing, 67, 105 explained, 107 starting and, 21 layer, 13 defined,6 main routines, 98 manager class, 103-104 minimum actions of, 25 msgAppClose and, 36 msgAppTerminate and, 36 name, 93 objects, 25-28 process, 24 active documents and, 23 destroying, 105 Notebook hierarchy and, 32 processCount equals, 0, 107 starting, 105 . publishing, 175 recovery, 16 releasing, 175 remote services and, 8 running, 23-24 shutting down, 38-40 stamping, 93-94 standard behavior, 12 starting, 21, 37-38 start-up, 106 state, 135 terminating, 38-40 windows, 29, 34 see also specific types ofapplications Application classes, 22, 26 clsHelioWorld, 125 creating, 103 Empty Application, 88 ·instances and, 24 method tables and, 99 PenPoint process and, 104 relationships of, 29 sections and, 34 well known, 104 see also Classes Application design. see Design guidelines Application development. see Development Application Framework, 1 application directories and, 28 bitmaps and, 171-172 creating instances, 103 defined,21 direction of, 24 document activation and, 24 Empty"Application and, 91 hierarchy, 40 hot mode and, 39 for housekeeping functions, 21 interactions, 26 layer, 12-13 defined,6 messages, 108 Notebook hierarchy and, 30 pre-existing classes, 20 resource file, 141 restoring documents and, 36 in running application, 20-21 SAMs and, 33-34 saved documents and, 36 turning a page and, 36, 135 Application programming interfaces (APIs) above kernel layer, 5 addresses, 6 characteristics, 5 AppMain, 25, 106 activating application and,' 107 AppMonitorMain, 105, 107 APP.RES, 168 application message resource, 169 creating icons, 172 Architecture functionality, 6 object-oriented, 5 Arguments, 99 Argument structure, elements, 50 Assertions, 85 ASSERT macro, 85 Automatic layout (User Interface Toolkit), 10 Basic Service, 272 Bit manipulation, 79 Bitmaps, 171-172 editor application, 172 Bookshelf, 5 Accessories icon, 95 BOOLEAN type, 77 Boot time, 94 Built-in classes, 116 Buttons. see menu, buttons C language compiler portability,76 unused parameters and, 110 function calls, 67 programming, 19 runtime, 7 source, 67 source code, 98 Calculator, 261-262 Character types, 62 288· INDEX string constants and. 62-63 CHAR types, 62-63: 77 versioning data and, 63 Checklist, 68 of non-essential items, 69 of required interactions, 68-69 Class browser, 118 counter, l38 creation, 103-104 hierarchy, 44 looking up, 119 info table, 55 names, 71, 79 UID, 102-103 Classes, 20, 41 Adder, 261 applications and, 24 mix of, 30 Basic Service, 272 built-in, 116 Calculator, 262 class info table and, 55 Clock, 263-264 code sharing, instead of, 43-45 component, 66 Counter Application, 196 creating, 53-58 timing of, 54 data structures and, 43 defining, 125 design guidlines and, 16 designing, 60 drawing contexts, 128 Empty Application, 177 extending existing, 44 Hello World (custom window), 187 Hello World (Toolkit), 181 Inputapp, 270 Installer and, 22 instance data of, 65 instances and, 48 learning about, 118 message arguments for, 54-55 messages, 44 Class Manager and, 99 defining set of, 55 overriding, 44 method tables, 55 identifying, 56 . MIL Service, 273 _NEW structure, 49-51 Notepaper App, 265 organizing into program units, 61 Paint, 266 . public, 175 registering, 175 sharing, 174 sources for, 61 subclassing, 5 Template Application, 251 Test Service, 272-273 Tic-Tac-Toe, 149-150,205 Toolkit Demo, 268 VI Toolkit, 18, 116-117 Writerap,271 see also Application classes; Object classes; View classes; Window classes Class implementation C files, 109 Class Manager, 7, 41-58 class and object use, 20 classes, 43-45 creating, 53-58 OsSymbolsInit, 162 code, 42 constants, 71-72 conversion functions, 163 data structures, 48-49 instance data and, 132, 140 macros and, 82 message handlers and., 99, 109 . messages, 42-43 sending, 45-48 method table, 47, 49 file, 56. msgDestroy, 109 msgDump, 161 msgRestore, 142 objects, 41-42 creating, 48-53 pointer, 47 parameters for function, 56 returned values and, 110 symbolic names and, 159 UID conversion, III use of, 76 Client, 42 clsCntr and, l38-l39 in creating object, 49 defined, 42 in initializing _NEW structure, 52 object communication, III window, 120 one, per frame, 122 see also Code Clock, 263-264 Closing file, 145 clsApp,16,26 application instances and, 120 descendant class of, 26 Empty Application, 88 initialization of, 97 message handling and, 108 msgAppChild and, 107 saving info and, 35 turning a page and, l35 window appearance and, 121 clsAppMgr,103-104. application placement and, 104 clsAppMonitor, 107 subclass, 107 clsBasicService, 272 clsBoarder, 116 clsClass,48,49,53-54 clsClockApp, 264 clsCntr, l35 getting and setting values, 139-140 instance data, l38-139 method table for, 138 object, 136 clsCntrApp, 136 instance data, 142-143 label and, l36 memory-mapped files and, 143 menu creation, 146 method table for, l37-138 msgSave, 141 clsCounter, 136 instance data and, 139 clsCustomLayout, 122· clsEmbeddedWin,155 clsEmptyApp;99,177 message handl,ing and, 107 ClsEmptyAppInit, 103 code sample, 108 clsField, 123 clsFoo,251 clsFrame, 121 clsGWin, 156,269-270 help gesture and, 166 clsHelloWin, 125 classes Used, 187 DC creation, l30, 131 DC state, 131 drawing and, 133 enhancements, l33 highlights, 127-128 instance data, l31-l32 accessing, l32 method table for, 125, 127 painting and, 129 ~tructure definition, 130 ClsHelloWinInit, 130 clsHelloWorld, 120, 125 classes used, 181, 187 highlights, 127 method table for, 125, 127, 181 clsInputApp, 270 clsIntegerField, l36 clsInWin, 269, 270 clsIP,269-270 clsLabel, 89 Clock and, 264 Hello World (toolkit) and, 117, 181 hierarchy, 119 inherited classes from, 122 INDEX msgNew arguments for, 118-119 style settings, 119 clsList,45 header file, 45 method table, 57-58 in UID.H, 53 CLSMGR.H,109 clsNote, 168 StdMsg customization, 171 clsNoteIconWin, 264 clsNotePaperApp, 265 ClsNum,156 clsObject, 51 data structure and, 49 instance of, 28 UID and, 52 ClsObjectToString, 163 clsPageNum, 136 clsResFile, 145 clsSectApp, 34 ClsSymbolsInit, 159, 162 dsTableLayout, 122 clsT emplateApp, 251 clsTkDemo, 268 clsTkTable, 146 clsTttApp, 149,205 instance data for, 153 clsTttData, 149,205 client tasks, 152 instance data for, 153 metrics, 153 stationary handling and, 165 clsTttView, 149, 154,205 input event handling and, 156 instance data for, 153 keyboard input, 158 quick help, 166-167 selections and, 155 clsView, 27-28 clsWin, 16 instance of, 27-28 clsXGesture, 156 CMPSTEXT.H, 64 CNTRAPP.C,199-203 CntrAppChangeFormat,147 CNTRAPP.H,199 CntrAppMenuBar/147 CntrAppRestore, 144 CNTR.C,197-199 CNTR.H,197 Code Application Framework and, 12 to create object, 51-52 entry point, 24 executing, 105 length, 76 loader and, 6-7 reusing, 5 run-through, 97-104 sharing, 16 application, 67 application layer, 13 classes instead of, 43-45 see also Client; Sample code Coding conventions, 14,70-72 Class Manager constants, 71-72 defines, 71 exported names, 72 functions, 71 suggestions, 76 typedefs, 70 variables, 70 Color model, 128 Command file, for Hello World (custom window), 126 Command invocation, 4 Comment headers, 74 Comments, 75 Compiler, 77-78 flags, 92-93 independence, 77 enumerated values, 78 function qualifiers, 78 switches, 78 Compiling Adder, 261 Basic Service, 272 Calculator, 262 Clock, 264 CounterApp, 137, 196 EmptyApp, 177 Hello World (custom window), 187 Hello World (toolkit), 181 Inputapp, 270 MIL Service, 273 Notepaper App, 265 Paint, 266-267 Template Application, 251 Test Service, 273 Tic-Tac-Toe,205 Toolkit Demo, 268 Writerap,271 Compiling and linking, 66-67 C source and header files, 67 Empty Application, 92-94 compiling application, 92-93 compiling method tables, 92 linking application, 93 stamping application, 93-94 HELLOTK, 114 HelloWorld (custom window) executable, 125-127 linker command files, 67 method table files, 66 SDK files, 67 289 WATCOM,66 Component, 20, 40 application, 149-150 classes, 66 layer, 12 defined, 6 for modular design, 15 Notebook, 29 VI Toolkit creating, 116-119 illustrated, 117 ComposerText routines, 64 Connections notebook, 8 for displaying application, 67 Constants, 71-72 basic, 77 Controls, general model, 123 Coordinates in drawing context, 129 system, 152 Counter, 135 class, 138 instance data, 138 value display of, 136 getting and setting, 139-140 CounterApp,136 clsCntr and, 136 compiling, 137 document page, 143 Hello World programs and, 136 installing, 137 instance data, 142-146 filing counter object, 145-146 memory-mapped file, 143 opening and closing file, 143-145 menu bar, 147 objects, 137 receiving msgRestore, 146 receiving msgSave, 145-146 Counter Application, 135-140 classes, 196 compiling, 137, 196 counter class and, 138 files, 196 getting and setting values, 139-140 highlights, 137-138 installing, 137 instance data, 138-139 objectives, 195 running, 196 sample code, 195-204 tutorial, 89 Counter object, 89 creating, 143-144 filing, 145-146 restoring, 146 saving, 145-146· CPU, power conservation and, 7 290 INDEX ctx, 99, 109 Cursor, 4 Custom window, 125 Data conversion/checking, 78 duplication, 15-16 encapsulation, 42 formats, 17 input, 4 interaction and view, 152-153 saving and restoring, 135-148 transfer, 12 types, 76-77 Data object clsViewand,27-28 design, 152-153 dumping, 161 saving, 153 separate stateful, 150 view and, 155 DbgFlagGet,86, 160 DbgFlagSet, 86 DB (source debugger), 133 Hello World (custom window) and, 134 messages, objects, status values, 162 speeding up debugging with, 133-134 DC (drawing context object), 128 creating, 129-130, 131 graphic state of, 128 UID,130 DOE (Dynamic Data Exchange) linking, 12 DEBUG, 81, 87 assertions and, 85 debug flags and, 85 dumping and, 161 preprocessor variable, 82 tracing and, 159-160 Debugf, 68, 84, III debug flags and, 160-161 Tic-Tac-Toe and, 159 Debug flags, 32, 85-86 B,96 0,112 Debugf statements and, 160-161 F1,97 F,97 ·G,97 sets, 160 Debuggers. see Mini-debugger; Source-level debugger Debugger stream, 85, 111-112 using output, III viewing output, 111-112 Debugging, 68, 159-163 assistance, 84-87 assertions, 85 debug flags, 85-86 printing debugging strings, 84-85 suggestions, 87 flag sets, 86 setting, 86 Hello World (custom window), 133-134 messages, 87 Penpoint 2.0 and, 63 strings, printing, 84-85 testing return values and, 115-116 Tic-Tac-Toe,159 #define, 74 for constants, 76 name, 50 Defines, 71 file section, 74 Deinstallation, of application, 38 Descendant classes, 25 of clsApp, 26 inheritance and, 43 for storing structured data, 28 Design guidelines, 15-18 Designing applications, 59-61 classes, 60 for internationalization and localization, 61-64 message handlers, 60 messages, 60 program units, 61 user interface, 60 Desktop, 33 floating accessories and, 34 parent window, 33 Destination application, 11 Development application, 59-90 checklist for, 68-69 cycles, 66-68 application installation, 67 debugging and linking, 66-67 general, 88 debugging, 68 key concepts for, 115 strategy, 64-66 component classes and, 66 displaying on screen and, 66 entry point, 65 instance data, 65 statefUl objects and, 65 Device codes, 156 multi-key input and, 158 Directory, 28 Desktop, 33 names, 29 Notebook, 33 section, 34 Disk Browser, 96 Diskless robot, 7 Disk Viewer, 32 DLC files, 127 DLL (dynamic link library), 15 components and, 40, 66 Hello World (custom window), 125 linking, 126 NotePaper, 265 program units and, 61 DLLINIT.C, 194 DLL.LBC file, 126, 194 DLLMain,66, 126 for clsHelloWin, 127 Documenting applications, 175 Document orientated design, 17 Documents, 23 activating, 23, 36-37, 105 application hierarchy and, 28 creating, 105 default names of, 103 deleting, 38 embedding, 34-35 Empty Application, 95 copying to hard disk of, 97 corkboard margin for, 97 embedding, 97 floating, 95 messages, 97 tabs for, 96 files/applications and, 37-38 as floating accessories, 104 life cycle of, 23 names of, 33 as Notebook pages, 104 off-screen, 24 restoring inactive, 36-37 running, 24, 94 for starting applications, 37-38 terminating, 36-37 Document state, duplication, 39 DPrimf, 68, 84, III debug flags and, 161 Drawing, in window, 133 see also System drawing context, 128 Dumping, objects, 161 EDA (embedded document architecture), 12, 13 Editing model, 11 Embedded applications, 34-35 Embedded documents, 28 Empty Applications and, 97 EMPTYAPP, 92 compiling, 177 INDEX documents, 26 objectives, 177 running, 177 see also Empty Application EMPTYAPP.C,91 debugger stream and, 111 main routine for, 100 msgDestroy, 108 parts of, 100 sample code, 178-179 EmptyAppDestroy,99 parameters in, 110 Empty Application, 91-112, 107-108, 107-109 application class and, 104-107 Application Framework and, 91 classes, 177 code run-through, 97-104 compiling and linking code for, 92-94 creating, 105 debugger stream and, 111-112 document copying to hard disk of, 97 corkboard margin for, 97 creation, 95 embedding, 97 floating, 95 messages, 97 tab for, 96 EmptyAppDestroy,99, 110 enhancements, 163 files, 91 used,177-178 floating, 95 icon, 95 installing and running, 94 instance, 106 message handler, 109-111 for msgDestroy, 109 method table, 91 file, 108 option sheet, 96 properties display, 95 sample code, 177-180 tutorial, 88 zooming, 95· see also EmptyApp Entry point, application, 65 Enumerated values, 78 Error~handling macros, 80, 81-84 StsChk,82 StsFailed, 82, 83 StsJmp, 82, 83 StsOK, 81, 82, 84 StsRet, 82, 84 Error messages, StdMsg and, 169 Even macro, 78 Exported names, 72 Export option card, 172 Extensibility, 14-15 Extensions, 70 Failures, dwing msgInit/msgRestore, 153 Fields, 123 File browser, 13 Filed state, 35 Files Adder, 261 Basic Service, 272 Calculator, 262 Clock,264 closing, 143 Counter Application, 196 Empty Application, 91, 177-178 format compatibility of, 17 header comments for, 73 Hello World (custom window), 188 Hello World (toolkit), 181 Inputapp, 270 memory-mapped, 143 MIL Service, 274 names of file system and, 7 STAMP and, 94 Notepaper App, 265 opening, 37-38, 143 for the first time, 143-144 to restore, 144-145 Paint, 267 reading from, 142 SDK,67 Test Service, 273 Tic-Tac-Toe, 205-206 Toolkit Demo, 269 Writerap,272 writing to, 141 see also Header files File structure, 72-76 . coding suggestions, 76 comments, 75 defines, types, globals, 74 file header comment, 73 function prototypes, 74 include directives, 73-74 indentation, 75 message headers, 75 File system, 7-8 class and object use in, 20 directories and, 28 document state and, 39 hierarchy, embedded applications and,35 Notebook hierarchy, 31 organization, 28-29 Filing, 28 counter object, 145-146 DC, 132-133 disadvantage of, 143 291 methods, 143 object, 129-132, 140-142 state, 135 FILLED.TXT, 205, 250 Flags, 79 checking, 112 debugging, 32, 85-86 setting values to, 86 Floating, 95 accessories, 34, 104 Font gesture, 174 scales, 127 FOO.C, 252, 257-259 FOO.H, 252, 256-257 FORMATFILE, 144 Frame object, 66 Frames, 27, 113 client windows and, 122 creating, 121 items included in, 120 toolkit window inside, 121 Functions, 71 prototypes of, 74 qualifiers, 78 Gestures, 4 fonts for, 174 handling, 156-157 handwriting and, 156-158 Globals,74 file section, 74 Global well-known DID, 53 GO.H,77 bit definition, 79 compiler independence and, 77 uppercase keywords and, 78 Graphical gestures. see Gestures Graphics, 9 overview, 128-129 see also ImagePoint; System drawing context GWin, 167 Handwriting, gestures and, 156-158 Handwriting translation (HWX) engine, 11 subsystem, 4, 10 Header files, 53 for class info, 118 common, 74, 102 C source and, 67 enum value and, function prototypes, 74 indentation, 75 i8 292 INDEX INTL.H,62 libraries and, 102 message header, 75 multiple inclusion, 73-74 structure, 72 see also Files HELLO.C, 188-190 HELLO.DLe, 195 HELLOTK1.C, 113, 181 code run-through for, 114-122 highlights of, 114-115 sample code, 182-184 testing well-known UID, 114 HELLOTK2.C, 113, 181 custom layout window, 123 enhancements, 123 HELLOTK1.C comparison, 122 highlights, 122-123 layout, 122-123 one client window per frame, 122 sample code, 184-186 HELLOTK, 113 compiling and linking, 114 things to do with, 114 HELLOWIN.C, 190-194 HELLOWIN.H,190 HelloWinInit,131 code, 131 Hello World (custom window), 125-128 classes, 187 clsHelloWin highlights, 127-128 clsHelloWorid highlights, 127 compiling, 187 linking and, 125-127 debugging, 133-134 DLC files and, 127 DLL files and, 126 drawing in window, 133 files, 188 font scales and, 127 modifYing, 133 objectives, 187 page turn, 132 running, 187 sample code, 187-195 tutorial, 89 window creation, 130 Hello World (toolkit), 113-123 classes, 117, 181 client window and, 120 code run-through for HELLOTKl.c, 114-122 compiling, 181 creating application instances for, 114 files, 181 HELLOTK, 113-114 installing, 114 label creation, 121 message sending, 115-116 method table, 114 msgAppInit, 114-115 running, 181 sample code, 180-186 second HELLOTK highlights, 122-123 testing, 181 tutorial, 89 UI Toolkit and, 113 see also UI Toolkit Help documents, creating, 165-166 gesture, 166 Notebook, 13,33 Tic-Tac-Toe,165-166 Quick, 166-168 systems, 13 text source files, 250 HELP subdirectory, 165 HELTBL.TBL,188 HELWTBL.TBL,188 Hierarchy, 29-32 application, 28-35 embedding and, 35 sections and, 28 Application Framework, 30, 40 class, 44, 119 Notebook, 29-32 Hot links, 12 Hot mode, 39 Housekeeping functions, 1 Icons, 42, 171-172 Accessory, 95 application and document, 171 creating, 172 Empty Application, 95 IDataDeref, 140 ImagePoint graphics, elements of, 9 imaging model, 9 class and object use in, 20 for printing, 9-10 system drawing context, 128 interface, 9 messages, 9 see also Graphics In:Out,50 message header and, 75 In, 50 message header and, 75 Inbox,5 networking and, 8 as notebook, 33 Include directives, 73 Indentation, 75 Information storage and retrieval. see Notebook metaphor Inheritance, 25 class, 43, 44-45 Initialization information, 118-119 routine, 98-99, 100-10 1 window, 131-132 Input distribution model, 11 event handling, 156 handling, 149-158 multi-key, 158 system translation, 10 windows and, 8 Inputapp, 269-270 InRange macro, 78 Installable software sheet, 22 Installation, 105 features, 163 MS-DOS, 21-22 PenPoint, 22 Installer, 22 application, 67 deinstallation with, 38 help documents and, 165 Quick Help and, 168 responsibilities, 22-23 Installing applications, 21, 67 CounterApp, 137 Empty Application, 94 Instance data, 55, 130 accessing, 132 application, 65 clsCntr, 138-139 CounterApp, 142-146 Hello World (custom window), 130 instance info vs., 153 . message handler and, 60 modifYing read-only, 140 saving in, 65 size, 54-55 updating, 140, 142 using, 132-133 Instances, 103 application classes and, 24 classes and, 43, 48 Class Manager messages and, 106 clsApp and, 120 initializing new, 49 objects and, 43 processing of, 47-48 Tic-T ac-Toe, 150 Interaction checklists, 68-69 INDEX Internationalization data types and, 77 defined,61 designing for, 61-64 preparing for, 63-64 INTL.H header file, 62 IsScaleFitWindowProper, 119 Loader, 6-7 database, 22 Load-time initializations, 76 Localization code modularization and, 64 defined,61 designing for, 61-64 Low-level pen events, 10 Kernel, 105 Kernel layer, 6-7 defined,6 services, 6 support features, 6-7 Keyboard handling, 158 input and selection, 153-155 PenPoint and, 11 keyCode, 158 Keywords, 78 w Label, 113, 114 clsCntrApp, 138 create, 119 when to, 121 HELLOTKl.C and, 114 for Tic-Tac-Toe, 151 lABEL.H, 119 Language-dependent routines, 64 Layer, 6 application, 6, 13 Application Framework, 6, 12-13 component, 6, 12 kernel,6-7 MIL (machine interface layer), 6,273-274 system, 7-12 Layout, 122-123 windows, 122 Legibility, 77 Libraries, 102 Linker, 93 flags, 92-93 linking DLLs and, 126 options, 93 Watcom, 126 Linking, application, 93 see also Compiling and linking LIST_NEW, 49 List object, 45 attributes, 47 code to create, 51-52 messages and, 46 see also Object "Live compound documents," 13 Iname,126 Macintosh computers, networking with, 8 Macros error handling; 80,81-84 extensions of, 76-84 stack trace and, 82 see also specific macros main (declaration), 98 activating application and, 107 complex explanation of, 106 initialization routine and, 100 . simple discussion of, 105-106 Main entry point, 22, 24 Main (function), 71 . Main notebook, 33 Main routine, 25, 98 calling, 98 declaration, 98 running process and, 104 Main window, 26 embedded applications and, 35 MAKEFILE, 91 for Counter Application, 203-204 for EmptyApp, 179-180 for Hello World (custom window), 194-195 for Hello World (toolkit), 186 for Template Application, 259-260 for TTT (Tic-Tac-Toe), 249-250 Makefiles, 92 Hello World (custom window), 127 Tic-Tac-Toe, 164, 165 MakeStatus, 80 MakeWarning, 80 MakeWKN,53 Manuals, 175 Mask application, 38 Max macro, 78 Memory checking, 112 conservation, 15 in application termination, 38-39 document state and, 39 mapping file to, 144 PenPoint programs in, 23 Memory-mapped files, 15, 89 to avoid duplication, 143 file system and, 7 293 Memory-resident file system, 16 Menu bar, 113 in CounterApp, 147 buttons, 113, 147-148 defining, 170 specifications for, 147 support, 146-148 see also SAMs (standard application menus) Message headers, 75 table, 56 tracing, 120 see also Messages Message arguments, 45-46 different expected, 122 instance data and, 138 msgNew, 118-119 msgRestore, 142 msgSave, 141 new class, 54-55 ObjectCall and, 47 as ObjectCall paramenter, 46 passing wrong, 121 specific structure of, 46 structure, 47, 51 Message handler, 55, 109-111 designing, 60 method table and, 99 for msgDestroy, 100 parameters, 99, 109-110 in EmptyAppDestroy, 110 privacy, 111 responses, 57 . status return values and, 110 Message handling, 107-109 method table and, 108 msgDestroy, 109 Messages, 41, 79 abstract, 57 advisory, 57 ancestor, 60 benefits of, 43 coding conventions for, 71 designing, 60 Empty Application, 97 handling, 44-45 instead of function calls, 42-43 objects and, 43 possible responses to, 57-58 selfUIDs and, 56-57 sending, 45-48 Hello World (toolkit) and, 115-116 methods of, 115 status values and, 47,52 Template Application and, 90 tracing, 159-160 Method. see Message handler 294 INDEX METHODS.TBL, 91, 99 Counter Application, 196-197 Empty Application sample code, 178 Hello World (toolkit), 182 Template Application, 252 TttApp, 206-207 Method table, 47~ 49, 55-58 CLASS_NEW message argument, 54 for clsCntr, 138 . for clsCntrApp, 137-138 for clsHelloWin, 125, 127 for clsHelloWorld, 125, 127 for clsList, 57-58 compiler, 55, 56, 92 header file and, 109 see also MT (method table compiler) compiling, 93 Empty Application and, 91 files, 55, ~8 creating, 66 suffix, 98 for Hello World (toolkit), 114 message handlers and, 99 names, 98 wild cards, 98 Metrics, 120 for controls, 123 MIL (machine interface layer), 6 Service, 273-274 Mini-debugger, 84, 85 for code crashes, 87 uses of, 111 see. also Debugging MiniText, 166 Min macro, 78 MKS toolkit, 162 MODNAME,93 Modular design, 15 MS-DOS file system compatibility and, 7-8 installation, 21-22 main routine, 25 networking with, 8 termination, 23 msg, 99,109 EmptyAppDestroyand, 110 msgActivateChild, 107 msgAppClose, 36, 132 clsCntrApp, 138 clsHelloWorld and, 127 on screen display and, 66 turning a page and, 36 msgAppCreateMenuBar,146 msgAppGetMetrics,120 msgAppInit, 65 handler, 121 - Hello World (toolkit) and, 114-115 msgAppOpen, 132 clsCntrApp, 138 clsHelloWorld and, 127 displaying on screen and, 66 msgAppTerminate,36 msgCntrGetValue,139 handler for, 139 pointer and, 140 msgCntrlnc,139 for incrementing value, 140 msgDcDraw, 133, 187 msgDestroy,99, 101 closing file and, 145 clsHelloWin and, 128 message handling and, 109msgDump,68 implementation, 161 Tic-Tac-Toe and, 159 msgFrameSetClientWin,120 msgFree, 109 closing files and, 143, 145 msgGWinGesture, 156 MsgHandler, 99, 110 naming pointer, 132 MsgHandlerParametersNoWarning, 110 MsgHandlerWithTypes, 99, 132 msgInit,65 clsHelloWin, 128, 130 failures during, 153 msgInputEvent, 156 msgLabelSetString, 42-43 msgListAddltemAt, 45 msgMemoryMap, 144 msgMemory¥apFree, 145 . msgNew, 48, 49 arguments for clsLabel, 118-119 clsCntr, 139 clsTkTable, 146 to create application class, 103 to create class, 53, 54 to create instance of class, 51 key value, 104 message argument value, 49 msgNewDefaults, 51, 52 before msgMew, 118 clsCntr, 139 clsTttView, 166-167 for initialization, 119 - MsgNum, 156 msgOpen,36 clsHelloWorld and, 127 msgResGetObject,145 msgResPutObject, 145, 146 creating resources, 167 msgRestore, 36, 140 failures during, 153handling, 142 message arguments to, 141 stateful objects and, 65 versioning data and, 63 msgSave, 35,140 closing files and, 143 clsCntrApp, 141 handling, 141-142 message arguments to, 141 stateful objects and, 65 msgStreamRead,142 msgStreamWrite,141-142 msgTrace, 159, 160 msgTttDataChanged,155 msgWinBeginRepaint,152 msgWinRepaint, 128, 129, 187 - repainting strategy and, 152 window objects and, 133 MT (method table compiler), 66 object and header files and, 92 see also Method table, compiler Multi-font component, 12 Multitaskingservice, 7 Names length of, 77 symbol, 162-163 Naming conventions. see Coding conventions Networking, 8 _NEW_ONLY structure, 50 for each class, 51 names for, 51 _NEW structure, 49 for clsCntr, 138 contents of, 118-119 identifying, elements, 51 initializing, 52 _NEW_ONLY for each class, 51 reading, definition, 50 typedef, 50 Non-stateful objects, 66 N
Source Exif Data:File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.3 Linearized : No XMP Toolkit : Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:56:37 Create Date : 2010:11:16 19:21:22-08:00 Modify Date : 2010:11:17 03:54:26-07:00 Metadata Date : 2010:11:17 03:54:26-07:00 Producer : Adobe Acrobat 9.4 Paper Capture Plug-in Format : application/pdf Document ID : uuid:cfca2c34-c5c6-46aa-ab10-8c4b1e485d07 Instance ID : uuid:4b695d33-c009-4c06-96fe-06710bc8683a Page Layout : SinglePage Page Mode : UseNone Page Count : 310EXIF Metadata provided by EXIF.tools