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 PDF.
Page Count: 310

DownloadPen Point_Application_Writing_Guide_Mar92 Point Application Writing Guide Mar92
Open PDF In BrowserView 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
finclude 
I 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                      : 310
EXIF Metadata provided by EXIF.tools

Navigation menu